SSE
一种基于 HTTP 的服务器向客户端单向推送数据的技术(EventSource / text/event-stream)。
[!info] related notes
SSE
一种基于 HTTP 协议的服务器向客户端单向推送数据的技术。
如果说 WebSocket 是一条“双向高速公路”,那么 SSE 就是一个“单向广播电台”。客户端打开收音机(建立连接)调到特定频道后,就只能被动接收电台(服务器)源源不断发送过来的节目内容,而不能用收音机向电台喊话。
随着 ChatGPT 等 AI 大模型的爆火,你看到的那些 AI 回复时“打字机式”逐字弹出的效果,绝大多数就是基于 SSE 技术实现的。
下面我们来详细拆解 SSE 的工作原理、特点以及它与 WebSocket 的区别。
一、 SSE 是如何工作的?
与 WebSocket 需要一套独立的复杂握手和帧结构不同,SSE 完全基于标准的 HTTP 协议,极其轻量和简单。
1. 建立连接
-
客户端向服务器发送一个普通的 HTTP GET 请求。
-
服务器接收到请求后,返回一个特定的 HTTP 响应头,告诉客户端:“我要开始流式传输数据了,请保持连接不要断开。”
其中最关键的响应头是:
Content-Type: text/event-stream(声明内容类型为事件流)Cache-Control: no-cache(禁止缓存)Connection: keep-alive(保持连接不断开)
2. 数据传输
连接建立后,服务器就可以随时向客户端发送文本数据了。SSE 的数据格式非常简单,纯文本,按行分割。每条消息由一个或多个字段组成,以两个换行符 \n\n 结束。
一个标准的数据块长这样:
id: 101
event: message
data: Hello, World!
data:核心字段,包含实际传输的数据。如果数据有多行,可以写多个data:。id:事件的唯一标识符。如果连接断开,浏览器会自动重连,并带上最后一次接收到的 ID(通过Last-Event-ID请求头),方便服务器接着推送未完成的数据。event:自定义事件类型。前端可以监听不同的事件名(默认是message)。
3. 自动重连(SSE 的超强天赋)
这是 SSE 相比 WebSocket 的一大杀手锏:浏览器原生支持断线重连。如果网络突然波动导致连接断开,浏览器底层的 EventSource API 会自动尝试重新连接服务器,不需要开发者手动写复杂的重连逻辑。
与其他实时通信方式的关系
SSE 本页只维护协议本体。SSE、WebSocket 与 Polling 在通信方向、基础设施、断线恢复和应用场景上的系统对比统一维护在 实时通信:SSE vs WebSocket vs Polling。
三、 典型应用场景
总的来说,只要你的业务需求是“服务器单向高频向客户端发消息,而客户端很少向服务器发消息”,首选 SSE。
- AI 大模型对话流(LLM Streaming): ChatGPT、Gemini 的逐字输出效果,这是目前 SSE 最火的应用场景。客户端只需发送一次 Prompt,服务器随后将生成的 Token 持续不断地用 SSE 推送回来。
- 实时通知/消息接收: 比如社交网站的系统通知、邮件到达提醒。
- 金融/股票实时行情: 前端只需要看最新的价格走势,不需要向服务器发送大量数据。
- 日志监控平台: 实时在网页端查看服务器滚动的系统日志。
- 赛事比分直播: 实时更新体育比赛的比分和文字直播。
四、AI 对话中的暂停与中断
在 AI 流式对话场景中,用户可能需要中途停止生成。这涉及前后端的状态同步问题:
- 前端用
AbortController取消 fetch 请求,触发后端req.on('close') - 后端感知连接关闭后停止模型流,避免 token 浪费
- 消息状态需要设计状态机(
idle / streaming / cancelling / cancelled / done / error) - 流式回调中每次 append chunk 前检查消息状态,防止旧 chunk 继续写入
详见 SSE 流式输出的暂停与中断。
学习检查
不看正文回答:
- SSE 为什么仍然是 HTTP 响应,而不是另一套独立双向协议?
text/event-stream中 event、data、id、retry 分别表达什么?- 为什么 SSE 事件必须按协议边界解析,而不能把任意网络 chunk 直接 JSON.parse?
- EventSource 原生重连与“业务事件不重复/不丢失”为什么不是同一件事?
- 前端 Abort 连接后,为什么不能自动推导服务端业务任务已经停止?
- 代理缓冲或压缩为什么可能制造“假流式”?
- 什么时候应该去 SSE vs WebSocket vs Polling 做方案选择,而不是继续在 SSE 本体里找答案?