SSE 比 WebSocket 更合适的场合更多的原因
一句话总结
SSE是没有结束的HTTP响应。因为不是新协议,所以代理、认证、重新连接、日志记录全部保持原样运行。
有什么不同
| SSE | WebSocket | |
|---|---|---|
| 协议 | 只是HTTP | Upgrade后单独 |
| 方向 | 服务器 → 客户端 | 双向 |
| 重新连接 | 浏览器自动 | 直接实现 |
| 继续追赶错过的东西 | Last-Event-ID内置 |
直接实现 |
| 认证 | 保持Cookie·头像不变 | 必须放在握手处 |
| 代理/LB | 保持原样 | 需要单独设置 |
| 数据 | 文本(UTF-8) | 文本 + 二进制 |
**如果客户端不需要继续向服务器说话,SSE几乎总是正确的。**通知、进度、仪表板更新,以及LLM令牌流都是如此。用户的输入是普通的POST发送到就可以了。
电线格式
Content-Type: text/event-stream于是,本文就这样出现了。
event: token
id: 42
data: 안녕
data: 여러 줄이면
data: data: 를 반복한다
: 이건 주석이다. 하트비트로 쓴다
retry: 3000
规则五行就结束了。
- **空行是活动的结束。**如果不发送空行,客户会认为活动还没有结束,并等待——“为什么什么都没有来”的第1个原因
data:如果有多个,就会通过连续执行成为一个字符串。event:是活动名称。省略的话messageid:给的话,浏览器会记住,但重新连接时Last-Event-ID返回标题retry:是重新连接等待时间(毫秒):开始时注释。没有任何意义,但字节流失,连接仍然存在
浏览器那一边有三行
const es = new EventSource("/stream")
es.addEventListener("token", e => append(e.data))
es.onerror = () => { /* 브라우저가 알아서 다시 붙는다 */ }
断开连接后**会自动重新连接,最后收到的id的Last-Event-ID放在头中发送。**如果服务器从那个id之后开始发送的话,用户也不会知道断开连接。要用WebSocket做同样的事情,必须亲自制作重新连接、去重、保证顺序。
所以为什么不行——缓冲
在SSE中实际经历的问题大部分都是一样的。
密码对了,但是画面上一次性出现。
中间有人正在收集回应。犯人有三个人。
**1. 反向代理。**nginx默认缓存响应。
proxy_buffering off; # location 에
或者在应用程序中用头条连接——X-Accel-Buffering: no.
2.压缩。 gzip 需要以块为单位合并才能压缩。Content-Encoding: gzip如果加上这个,流媒体实际上就会死掉。SSE响应在压缩中被排除在外。
3.框架。 误认为创建响应并一次性返回的代码是流式传输的情况。将生成器yield不做,做一份清单然后还给我,就只是一个很大的回应。
诊断是按时间进行的。curl -N附着在上面看看第一个字节什么时候来。如果全部制作完成后才来的话,就是在收集什么东西。-N是关闭curl自己缓冲器的选项。
没有心跳就会断开
路由平衡器·代理有闲置超时(一般60秒)。如果没有任何数据流,就会断开连接。所以定期流出一行注释。
: ping
**客户不把这个看作是活动。**只流过字节。15~30秒的间隔就足够了。
连接次数限制
在HTTP/1.1中,浏览器限制为每个原生端同时连接6个。一个SSE会永久占用其中一个。打开六个标签,第七个标签的请求都会停止。**使用HTTP/2就会消失的问题(多路复用)。如果在生产中使用SSE,HTTP/2不是选择。
服务器方面忘记的事情
即使客户端断开连接,发电机也会继续运转——如果没有注意并停止的话。每次用户关闭标签,服务器都会有一项僵尸任务累积起来。
async def gen():
try:
while True:
yield {...}
finally:
await cleanup() # 끊길 때 반드시 여기로 온다
finally粘贴应该成为习惯。
现场诊断的顺序
如果收到“代码正确,但画面一次性出现”的举报,在修改服务器代码之前,先切断流到哪里。
curl -N http://앱주소/stream—直接粘在应用程序上。-N是关闭curl自己输出缓冲区的选项。如果删除这个,可能会因为工具而怀疑服务器。- 通过代理再次发送同样的请求。如果只在这里聚集,犯人就会出现缓冲或压缩。
- 浏览器开发者工具Network选项卡的EventStream——事件是否一个一个地到达
一步一步缩小范围,几分钟内就能确定服务器、代理、客户端中需要修理哪一个。在诊断时,首先怀疑的是测量工具本身。
什么时候是WebSocket呢
- 客户端经常发送(聊天输入、游标位置、游戏)
- 需要二进制(音频、屏幕共享)
- 往返延迟以毫秒为单位很重要
除此之外,SSE的运营要便宜得多。以连接中断时什么会自动恢复为标准选择的话,一般都会得到答案。