流式长连接为什么会中途断掉
服务端推送(SSE)依赖一条长期保持的 HTTP 响应。链路上的网关、负载均衡、CDN 都会统计「多久没有数据流过」,超过各自设定的空闲阈值就把连接判为死连接主动回收。长生成任务里可能出现几十秒没有内容输出的阶段(比如思考、检索、工具调用),这段时间恰好落在判空闲的窗口内,于是客户端看到的是回答中途静默然后报错。
基本信息
| 协议基础 | HTML 规范定义的 Server-Sent Events,基于 HTTP 长响应 |
|---|---|
| 心跳载体 | 以冒号开头的注释行,不进入数据通道 |
| 生效层次 | 应用层心跳,与 TCP 保活机制是两回事 |
| 间隔取值 | 常见做法是十几秒到半分钟,需小于链路最短空闲阈值 |
| 客户端要求 | 解析器需能忽略注释行,官方实现均支持 |
典型使用场景
- 长生成任务:推理中间存在较长的静默期,需要持续证明连接存活
- 经过多层代理:请求路径上有 CDN 或企业出口代理时,各层超时都要一起考虑
- 移动与弱网客户端:连接容易被网络中间设备回收,心跳能维持会话
- 长轮询替代方案:需要服务端在无数据时也维持响应通道的场景
- 排查断流原因:把心跳间隔与各层超时对照,能快速定位是哪一层在切连接
心跳行与各层配合
心跳行与各层配合
: ping
(以冒号开头的行为 SSE 注释,数据通道不受影响)
链路各层空闲阈值需大于心跳间隔:
客户端读超时 > 心跳间隔
反向代理读超时 > 心跳间隔
CDN 空闲超时 > 心跳间隔
推荐做法:心跳间隔取链路最短阈值的二分之一到三分之一断流点定位
| 现象 | 可能所在层 | 核对方式 |
|---|---|---|
| 静默固定时长后中断,数值稳定 | 某一层的空闲超时被触发 | 用固定间隔心跳,观察实际断开的时间点 |
| 只有经过 CDN 的域名中断 | CDN 侧空闲超时更短 | 对比直连与经 CDN 两条路径 |
| 低并发正常、高并发中断 | 连接数或资源限制而非超时 | 核对连接数与文件描述符上限 |
| 日志显示上游连接被关闭 | 反向代理读超时先触发 | 检查代理的读超时配置 |
| 客户端本地中断 | 客户端自身读超时或网络切换 | 抓客户端侧日志确认 |
心跳间隔参考链路最短的空闲容忍度:上线前把 CDN、代理与客户端的超时值扫一遍对齐,任何一层短于心跳间隔都会成为断流的那一环。
验证方法:在最长的真实链路上跑一次超长生成任务,确认实际断开时间点大于任务时长,而不是只在内部网络里测试。