SSE 服务器不需要推送时怎么办?核心答案很直接:别把“无消息”当成故障,让连接保持轻量空闲,用注释帧做心跳,用超时配置和断线重连兜底,必要时主动关闭空闲连接并让客户端凭 Last-Event-ID 续传。
SSE 服务器不需要推送时怎么办:先把“空闲”设计成正常状态
SSE 本质上是 HTTP 长连接,服务端打开响应流后,可以一直保持,也可以隔一段时间写一点内容,没有业务消息时,不必硬造业务事件,多数情况下,发一个注释帧就够了。
空闲不等于断开,SSE 通道本来就可以“等”
浏览器里的 EventSource 收到 text/event-stream 后,会持续读取服务端输出,服务端不写 data:,就不会触发前端的 message 事件,这个特性很适合低频推送场景。
- 业务没有新消息时,保持连接打开即可。
- 需要保活时,发送注释帧:
keep-alivenn。 - 注释帧以冒号开头,浏览器不会把它当成业务事件。
- 还可以发送
retry: 5000nn,告诉客户端断线后大约 5 秒再重连。
什么时候什么都不用发
局域网内部系统、短时间空闲、客户端本来就有重连机制时,可以暂时静默,但前提是链路中的网关、负载均衡、CDN 不会过早回收空闲连接。
- 空闲只有几十秒,且链路超时足够长,可以静默。
- 服务端无状态,客户端能自动重连,可以少发心跳。
- 业务允许延迟,客户端也能接受重新拉取,可以静默。
什么时候必须做动作
一旦链路里存在 Nginx、云负载均衡、CDN、移动端 NAT,空闲连接就可能被悄悄回收,这时心跳不是可选项。
| 场景 | 是否发心跳 | 推荐动作 | 主要风险 |
|---|---|---|---|
| 局域网短空闲 | 可不发 | 保持流打开 | 网关超时 |
| 国内云服务器加负载均衡 | 建议发 | 定时发注释帧 | 空闲连接被回收 |
| 移动端弱网 | 建议发 | 注释帧加 retry | NAT 超时、断网 |
| 连接数很高 | 控制频率 | 主动关闭空闲订阅 | 文件描述符和内存占用 |
业内专家指出,长连接的空闲管理往往比消息发送本身更影响稳定性,心跳太频繁会浪费资源,心跳太慢又会被中间设备切断。
国内云服务器 SSE 长连接超时怎么设置才不误伤业务
很多人遇到 SSE 断连,第一反应是代码问题,实际排查下来,常见原因是 Nginx 或云负载均衡的闲置超时。Nginx 的 proxy_read_timeout 默认常见为 60 秒,如果心跳间隔大于这个值,连接就可能被关掉。
Nginx 反向代理配置
下面是一段常见配置,重点是关闭缓冲、拉长读写超时、使用 HTTP/1.1。
location /events {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_set_header X-Accel-Buffering no;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
chunked_transfer_encoding on;
}
配置后不要只看 Nginx,云负载均衡、CDN、WAF 也可能有闲置超时,据公开文档,多数云负载均衡的闲置超时在几十秒到几分钟之间,具体以控制台为准,最终心跳间隔要小于这些超时中的最小值。
应用服务器和网关要注意什么
- Node.js、Java、Go 都要检查写超时和空闲超时。
- 每个 SSE 连接都会占文件描述符、内存和连接对象。
- 连接数较大时,先调系统限制,
ulimit -n。 - 内核参数可关注
net.ipv4.tcp_keepalive_time,但不要把它当成唯一保活手段。 - 如果使用线程模型,一个连接占一个线程会很快遇到瓶颈。
客户端重连和续传
EventSource 自带重连,服务端要做的,是让重连之后能接上之前的位置。
- 服务端发送事件时带上
id:,id: 1024ndata: {...}nn。 - 浏览器断线重连时,会自动带上
Last-Event-ID: 1024。 - 服务端读取这个请求头,从事件表里查询大于 1024 的消息。
- 如果没有新消息,可以保持静默,或发送注释帧
no-new-eventsnn。 - 客户端收到重复事件时,要用事件 ID 去重。
验证链路是否正常,可以用命令直接观察:
curl -N -H 'Accept: text/event-stream' http://your-domain/events
如果连接一段时间后断开,再看 Nginx 错误日志里的 upstream timed out,基本就能定位到超时配置。
SSE 和 WebSocket 空闲连接管理对比:低频推送场景怎么选
SSE 和 WebSocket 都能做长连接,但空闲管理思路不同,行业共识认为,SSE 在低频单向推送场景下的运维复杂度低于 WebSocket,如果你的场景只是服务器通知客户端,SSE 通常更省心。
对比表
| 维度 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 服务器到客户端 | 双向 |
| 协议基础 | HTTP/1.1 或 HTTP/2 | 独立协议升级 |
| 空闲保活 | 注释帧 | ping/pong |
| 自动重连 | EventSource 内置 | 通常自己实现 |
| 代理友好度 | 较高 | 部分代理需额外配置 |
| 适合场景 | 通知、消息、进度 | 聊天、游戏、协同编辑 |
空闲时策略差异
- SSE 空闲时发
hbnn,不触发业务事件。 - WebSocket 空闲时发 ping 帧,等 pong 回复。
- SSE 断线后浏览器自动重连,服务端配合 Last-Event-ID。
- WebSocket 断线后要自己维护重连、心跳、去重和补偿。
- 只有服务器推、低频、文本为主,优先考虑 SSE。
聊天应用无消息时 SSE 怎么处理:心跳、注释帧与事件补偿
聊天应用是最典型的 SSE 场景,没有新消息时,服务端不能一直发空消息,否则前端会收到大量无意义事件,正确做法是心跳归心跳,业务归业务。
心跳设计
Node.js 里可以这样写:
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no'
});
res.write('retry: 3000nn');
const hb = setInterval(() => {
res.write(': hbnn');
}, 25000);
req.on('close', () => {
clearInterval(hb);
});
这里每 25 秒发一次注释帧。心跳间隔要明显小于链路中最小空闲超时,否则保活没有意义,不要发 event: messagendata: {}nn,因为前端会被迫过滤空消息。
事件补偿
- 消息表至少保留
id、conversation_id、seq、created_at。 - 客户端保存最后收到的
lastEventId。 - 重连时请求
/events?lastEventId=123。 - 服务端查询大于该 ID 的消息,按顺序推送。
- 没有新消息时,保持静默或发注释帧。
- 客户端离开页面时调用
EventSource.close()。 - 服务端监听
close事件,清理定时器和订阅关系。
如果连接数很大,可以对长时间无活动的连接主动关闭,客户端下一次需要时再重连,凭 Last-Event-ID 补数据,这样比无限保留空闲连接更省资源。
自建 SSE 与云推送服务价格对比:空闲连接的成本账
自建 SSE 看起来免费,实际有服务器、带宽、运维和调优成本,云推送服务看起来省事,但连接数、消息量、请求次数都可能计费,价格没有统一答案,要看规模和地域。
自建成本
- 服务器费用。
- 公网带宽费用。
- 文件描述符、内存、端口占用。
- 运维排查长连接问题的时间。
- 高可用、跨地域、监控告警的额外投入。
近年来,移动端和云负载均衡对空闲长连接的回收更积极,连接数一多,空闲连接也会成为实打实的成本。
云推送服务成本
- 部分平台按连接时长计费。
- 部分平台按消息条数或请求次数计费。
- 免费额度通常适合测试和小规模。
- 空闲连接可能不计消息费,但会计连接时长。
- 跨地域、合规、审计需求会推高成本。
选择建议
- 小规模、内部系统、低频通知,自建 SSE 更直接。
- 大规模、跨地域、需要 SLA,评估云推送服务。
- 如果不需要推送,最省钱的做法是关闭不必要连接。
- 客户端按需订阅,服务端按需推送,比长期空转更划算。
收束
SSE 服务器不需要推送时怎么办?让空闲成为正常状态,心跳只做保活,业务事件按需发送,超时重连和 Last-Event-ID 做兜底,真正要避免的,是把空消息当心跳,把超时当故障。
Q&A:SSE 服务器不需要推送时怎么办
问题1:SSE 服务器长时间不推送,连接会不会自己断?
会,常见断点在 Nginx、云负载均衡、CDN、移动端 NAT,解决方式是发送注释帧 hbnn,并让心跳间隔小于链路最小空闲超时,客户端 EventSource 会自动重连,服务端配合 Last-Event-ID 补数据。
问题2:没有业务消息时,发空 data 当心跳可以吗?
不建议。data: 会触发前端的 message 事件,业务代码必须额外过滤,注释帧以 开头,不会触发 message 事件,是更干净的心跳方式。
问题3:SSE 和 WebSocket 哪个更适合不需要频繁推送的场景?
低频单向推送、只需要服务器到客户端,SSE 更合适,它基于 HTTP,代理友好,自动重连,双向通信、二进制、高频交互才选 WebSocket。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/689796.html




