SSE使用text/event-stream响应,在一条HTTP连接上持续发送文本事件。它没有WebSocket升级握手,但需要代理及时把小块数据交给客户端。最典型症状是服务端日志显示每秒发送,浏览器却十几秒后一次收到一批,这通常是某一层缓冲。
SSE链路的关键环节
| 环节 | 可能问题 | 验证方法 |
|---|---|---|
| 应用写入 | 框架没有Flush | 检查写入时间和Socket发送 |
| 压缩中间件 | 等待更多数据形成压缩块 | 对事件流禁用或调整压缩 |
| 反向代理 | 响应缓冲、缓存与超时 | 检查代理日志和流式配置 |
| CDN/WAF | 不支持长流或有固定超时 | 绕过受控测试并查产品限制 |
| 浏览器 | 重连、连接数和页面生命周期 | 查看Network时间线 |
响应头应满足什么
服务端通常返回Content-Type: text/event-stream,并根据需要设置防缓存头。连接保持打开,事件以规范格式和空行分隔。不要为流式响应预先设置错误的Content-Length。
代理缓冲为何造成批量到达
反向代理为了提高普通响应效率,会先读取一定数据再发送。对SSE,这会增加可见延迟。应只针对事件流路由调整缓冲,不要全站关闭,否则慢客户端会长期占用上游连接并增加资源压力。
压缩为什么也会延迟
压缩器可能等待更多字节以提高压缩率。SSE事件通常较小,等待会让实时性变差。可对事件流关闭压缩,或确认当前框架与代理支持及时Flush。应通过网络抓取验证块到达时间,而不是只看应用日志。
心跳解决什么问题
注释行或轻量心跳可以防止代理、NAT和负载均衡器把完全空闲连接回收,也帮助客户端识别链路。心跳间隔应小于最短空闲超时并留余量,但规模大时每次心跳会乘以连接数,需要做容量评估。
读取超时如何设置
代理读取超时通常关注两次上游数据之间的间隔。若业务事件可能很久不发生,心跳需在超时前发出。客户端、CDN和应用也可能有更短限制,所有层应协调,不能只改Nginx。
Last-Event-ID怎样支持恢复
每个事件可带稳定ID。连接断开后,浏览器可在重连时提供最后事件ID,服务端据此重放后续事件。前提是服务端保留有限历史并保证ID单调或可定位。没有事件存储时,重连只会收到新事件。
重复事件如何处理
断线发生在客户端收到事件但未保存状态时,重连可能重放同一事件。消费者应按事件ID幂等处理。不能把SSE传输视为严格恰好一次;业务写操作应有独立去重。
HTTP/2有什么影响
HTTP/2可在一条连接上承载多个流,缓解浏览器对同源HTTP/1.1连接数限制,但仍有流量控制、代理支持和单连接故障域。是否使用HTTP/2应从实际链路验证,不是SSE实时性的唯一条件。
连接规模如何估算
- 并发SSE连接数;
- 每连接心跳与事件频率;
- 代理文件描述符和内存;
- 上游连接与HTTP/2流数量;
- 断线后同时重连峰值;
- 负载均衡与发布时连接迁移。
排查顺序
- 在应用端记录事件写入和Flush时间;
- 检查浏览器单个数据块到达时间;
- 逐层排除压缩、缓冲和缓存;
- 记录每层空闲与总时长限制;
- 加入合理心跳并保持长时测试;
- 模拟断线,验证Last-Event-ID与去重;
- 压测连接与重连峰值。
延伸阅读
多层网关的缓冲与超时定位,可结合Nginx 504与代理缓冲排查查看。
结论
SSE不实时通常不是协议本身慢,而是应用Flush、压缩或代理缓冲。逐层测量数据块时间,再配置心跳、恢复和容量,才能让长连接在真实网络中稳定工作。






