有一种现象很容易误判为“模型慢”:服务端日志显示响应已经开始,浏览器却过了很久才一次性出现整段文字。排查时先分清首事件从模型接口到达的时间、网关把首事件写出的时间、客户端收到首事件的时间。三个时间点之间哪一段变长,才是下一步要查的层。
可以先在本机起一个纯模拟的事件流,不连接任何真实模型。它每隔一秒发一条事件,并在末尾发送当前演练协议的结束标记。再用 curl -N 直连模拟服务,确认本机能逐条看到;然后通过待验证的反向代理走同一路径。若直连逐条到达,代理路径却最后一起到达,就去检查代理对该路由的缓冲、压缩和超时配置。不要把流式接口的等待上限直接套用普通 JSON 请求的超时设置。
下面的本地服务只演示 SSE 传输时序,事件名和结束标记都是演练值,不能当成任意模型 API 的正式协议:
// stream-demo.mjs;Node.js 18+ 内置模块,无需安装依赖
import http from 'node:http';
http.createServer((request, response) => {
if (request.url !== '/demo') {
response.writeHead(404).end();
return;
}
response.writeHead(200, {
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache',
});
let count = 0;
const timer = setInterval(() => {
count += 1;
response.write(`event: demo\ndata: ${Date.now()} - chunk ${count}\n\n`);
if (count === 3) {
clearInterval(timer);
response.write('event: end\ndata: done\n\n');
response.end();
}
}, 1000);
response.on('close', () => clearInterval(timer));
}).listen(8787, '127.0.0.1');在终端 A 运行 node stream-demo.mjs,终端 B 运行 curl -N http://127.0.0.1:8787/demo,观察三条数据是否分秒到达。之后将自己的测试代理路由指向该服务,再用 curl -N 请求代理地址。若两条路径表现不同,保留代理配置版本和时间戳;若都逐条到达,继续检查前端事件解析与渲染节流。测试结束后停止本地服务,不要把这个演练端点暴露到公网。
记录表可用四列:路径、服务端写出时间、客户端收到时间、连接关闭原因。测试时用同一台机器、同一网络和同一模拟响应,先排除模型生成速度的波动。确认代理没有缓冲后,再换成真实 API 请求,并记录首事件和终止事件是否符合该接口规范。HTTP 200 只能说明响应开始,不能证明流式内容完整结束。
如果终端能逐条收到、网页却一次性渲染,还要看浏览器端:是否把 response.text() 用在整个流上,是否在循环结束后才更新状态,是否在每个字节块上重复创建 TextDecoder。网络字节块不保证与 SSE 事件一一对应,客户端需要持续解码并缓存到完整事件边界。用同一段模拟输出做“每次一条事件”和“把一条事件拆成几块”两次测试,最终应得到相同事件序列。若只有拆分版失败,问题就在本地解析,不必先更换接入服务。
当应用接入 NexoToken 或其他中转服务时,应在自己的服务、代理和客户端分别留必要的时间戳,避免将任何一层的停顿都归因于中转站。NexoToken 是否支持某个接口的流式格式,以当前公开文档和账号测试为准。本文的方法用于定位瓶颈,不提供未经测量的延迟排名。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。