简介:SSE(Server-Sent Events)是基于HTTP协议的服务器推送技术,也是AI大模型实现逐字流式输出的关键支撑。本文系统讲解SSE的定义、消息格式、工作原理、与WebSocket的区别、典型应用场景及接入注意事项,帮助你在单向推送场景下做出更轻量、更可靠的技术选型。
SSE(Server-Sent Events,服务器推送事件)是一种基于HTTP协议的服务器推送技术:客户端向服务端发起一次普通的HTTP请求后,服务端不立即结束响应,而是保持连接打开,把数据以“流”的形式持续不断地写回客户端。
理解SSE,可以抓住一个极简公式:
SSE = HTTP扩展字段 + Keepalive长连接
具体可以从三个视角展开:
它解决的根本问题是:传统HTTP“客户端问、服务器答”的一问一答模型下,服务器无法主动向客户端传递新数据。SSE以极低的改造成本,让服务器拥有了“主动开口说话”的能力。目前几乎所有主流浏览器都原生支持SSE,客户端无需引入任何第三方库即可直接使用,这是它区别于许多推送方案的重要优势。
1. 传统轮询模式的困境。 在SSE被广泛使用之前,客户端想“及时”拿到服务端最新数据,通常只有两种选择:短轮询与长轮询。短轮询是客户端按固定间隔反复发请求,延迟受轮询间隔限制,且绝大多数请求都是无效请求,服务端压力与带宽浪费随客户端数量线性增长;长轮询是服务端把请求“挂住”,直到有新数据才返回,实时性接近推送,但实现复杂,连接管理与服务端并发压力都很棘手。两者本质上都是在“用请求模拟推送”,成本高、体验差。
2. WebSocket对单向场景而言“能力过剩”。 WebSocket提供了真正的全双工双向通道,但它需要从HTTP升级到独立协议,链路上的网关、代理、鉴权都要额外适配;客户端还需自行实现心跳、重连、消息补偿等一整套可靠性逻辑。如果业务只需要“服务器向客户端单向推送”,引入WebSocket相当于为只需要单行道的场景修了一条双向八车道。
3. 大模型时代的引爆点。 大语言模型的生成过程有两个鲜明特性:一是逐个token(词元)渐进生成,二是长回答的推理耗时可能达到数秒甚至数十秒。如果等全部内容生成完毕再一次性返回,用户将面对漫长的空白等待;而SSE让模型“生成一点、就推送一点”,首字出现的等待时间被压缩到接近推理启动的时间,整体阅读体验接近真人打字。同时,一次对话的信息流向天然是“客户端提交一次提示词 + 服务端单向持续输出”,与SSE的单向模型严丝合缝。因此,当下主流的AI对话应用普遍采用SSE或类SSE的流式方案来完成逐字输出。
1. 专用的HTTP响应头。 服务端开启SSE,只需在响应头中声明:
Content-Type: text/event-stream // 声明这是一个事件流Cache-Control: no-cache // 禁止任何环节缓存Connection: keep-alive // HTTP/1.1下保持长连接
服务端返回200状态码后不结束响应体,以流式(通常为分块传输)方式持续写入数据,这条连接就变成了一条推送管道。
2. 标准化的消息报文格式。 SSE规定了四种字段,每条消息由若干“字段: 值”行组成,消息之间以空行分隔:
此外,以冒号开头的行会被当作注释忽略——这个特性常被用作“心跳”载体。示意报文如下:
: heartbeat <- 注释行,用作保活心跳id: 1event: tokendata: 你id: 2event: tokendata: 好
3. 浏览器原生的EventSource接口。 客户端零依赖,标准用法如下:
const es = new EventSource('/api/stream', { withCredentials: true });es.onopen = () => console.log('连接已建立');es.onmessage = (e) => console.log('默认消息:', e.data);es.addEventListener('token', (e) => appendToPanel(e.data)); // 监听自定义事件es.onerror = () => console.log('连接异常,浏览器将自动重连');// es.close(); 主动关闭连接
4. 内建的重连与续传机制。 连接意外断开时,EventSource会自动尝试重连,并把最近收到的消息id通过Last-Event-ID请求头带回;服务端据此从断点继续推送,实现“消息不丢”。这套可靠性逻辑由浏览器内建,无需开发者手写,是SSE相对自研推送方案的显著工程优势。
补充:AI应用中的常用变体——POST + 流式读取。 标准EventSource只支持GET请求,而AI对话往往需要提交很长的上下文,GET的URL长度无法胜任。因此业界普遍做法是:用通用的fetch接口发起POST请求,直接流式读取响应体,再按SSE的报文规范自行解析——消息格式不变,只是“订阅端”从EventSource换成了流式读取器。另外,流式响应没有“响应自然完成”的信号,业界常约定一个特殊的结束哨兵消息(例如一条表示“流已结束”的标记数据)来告知客户端收尾。
服务端伪代码(语言无关):
设置响应头:Content-Type: text/event-stream; Cache-Control: no-cache循环:若有待推送数据 -> 写入 "id: n\nevent: xxx\ndata: ...\n\n"每隔 N 秒 -> 写入 ": keepalive\n\n"
SSE vs WebSocket(最容易混淆的一对):
| 维度 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器→客户端) | 全双工双向 |
| 协议基础 | 标准HTTP | 独立协议,需从HTTP升级 |
| 重连机制 | 浏览器内建自动重连+断点续传 | 需自行实现 |
| 数据格式 | 仅UTF-8文本 | 文本与二进制均可 |
| 基础设施兼容 | 复用HTTP链路,网关/代理/鉴权基本无感 | 各环节需额外适配 |
选型建议:只需要服务器向客户端推送,优先SSE,实现更简单、链路更友好;客户端也要高频主动发消息(如协同编辑、在线游戏、音视频信令),则选WebSocket。
SSE vs 轮询/长轮询:轮询是“客户端反复问”,延迟与开销随频率走高;长轮询接近实时但复杂且占用服务端并发;SSE一次连接持续推送,在实时性、资源效率与实现成本上综合最优。
回到定义本身:SSE是基于HTTP的服务器单向推送技术——用HTTP扩展字段声明一条事件流,用Keepalive长连接承载持续推送,用标准化报文格式保证跨端互通,用内建重连机制保证可靠性。它的核心价值在于“以最小的成本获得实时推送能力”;它的适用边界同样清晰:单向、文本、轻量。当下AI大模型的逐字输出让这项低调多年的技术走到台前,而在通知、日志、监控、进度推送等传统场景中,它也始终是比WebSocket更简单务实的选择。记住“单向推送优先SSE,双向交互再上WebSocket”,就抓住了这项技术选型的本质。