很多人第一次接触 SSE,会先记住一个结论:它是服务端向浏览器推送消息的一种方式。
这个结论没有错,但还不够。真正重要的问题不是“SSE 是什么”,而是:
在什么业务场景里,SSE 比普通轮询更自然,又比 WebSocket 更克制?
如果只从技术名词出发,SSE 很容易被理解成“低配版 WebSocket”。但从业务场景出发,它的定位会清楚很多:SSE 适合服务端持续播报状态,浏览器主要负责接收、展示、恢复和收尾。
它不是为高频双向交互准备的,而是为“一个请求发起后,服务端不断告诉你进展”这类业务准备的。
场景一:AI 对话与文本生成
AI 对话是现在最典型的 SSE 场景。
用户提交 prompt 后,服务端不会等完整答案生成完才返回,而是把模型生成的 token 或文本片段一段段推给前端。前端拿到片段后逐步展示,于是用户能立刻感知“系统正在回答”,而不是盯着一个 loading 等几十秒。
这个场景里,SSE 的价值不是让接口更酷,而是改善交互确定性:
- 用户能看到内容正在增长。
- 用户能在生成过程中取消。
- 页面能区分“正在连接”“正在生成”“正在恢复”“已完成”。
- 断线后可以尽量从已生成位置继续。
- 重复片段不会让答案重复渲染。
AI 场景的难点也在这里。流式输出不是简单地把字符串拼起来。前端还需要识别每个片段属于哪个 conversationId、哪个 messageId、第几个 chunkIndex,并且处理重复、乱序、断线、完成事件和取消事件。
如果没有这些业务字段,SSE 只能提供“有东西来了”的传输能力,不能保证 UI 展示的是一份完整、唯一、连续的回答。
所以,AI 场景里真正可靠的做法是把流拆成事件协议:
POST 创建会话
POST 创建生成任务
GET 建立 SSE 流
event: chunk 持续返回增量内容
event: done 明确结束
event: error 表达业务失败
event: cancelled 表达取消
这里的关键判断是:SSE 负责推送,业务协议负责可信。
场景二:任务进度与后台作业
第二类常见场景是任务进度。
比如文件导入、批量审批、报表生成、视频转码、数据同步、批量发送通知。这些任务有一个共同特点:用户发起动作后,服务端需要一段时间才能完成,并且中间过程对用户有价值。
如果用普通轮询,前端可能每隔几秒请求一次进度。这样能工作,但会带来几个问题:
- 请求次数多,很多请求只是为了确认“还没变化”。
- 进度更新不够及时。
- 多个任务同时存在时,轮询节奏容易混乱。
- 页面离开、刷新、重进时,状态恢复需要额外设计。
SSE 更适合这种“服务端有变化时告诉我”的模型。任务开始后,前端建立一条事件流,服务端按阶段推送:
event: progress
data: {"taskId":"import-001","percent":30,"stage":"parsing"}
event: progress
data: {"taskId":"import-001","percent":70,"stage":"writing"}
event: done
data: {"taskId":"import-001","resultUrl":"/reports/import-001"}
这里不要只推 percent。真实业务里,用户关心的不只是百分比,还包括当前阶段、已处理数量、失败数量、能不能取消、失败后是否能重试。
一个更有用的进度事件,通常应该包含:
| 字段 | 用途 |
|---|---|
taskId | 确认事件属于哪个后台任务 |
stage | 表达当前业务阶段,例如解析、校验、写入、完成 |
processedCount | 已处理数量 |
totalCount | 总数量 |
failedCount | 失败数量 |
retryable | 失败后是否可重试 |
updatedAt | 调试和排序依据 |
任务进度场景的核心不是“实时”,而是“可解释”。用户不应该只看到一个进度条,还应该知道系统卡在哪里、是否还在推进、失败时下一步能做什么。
场景三:日志、监控与运行状态
日志流也是 SSE 的天然场景。
例如部署日志、构建日志、数据同步日志、设备运行状态、风控规则命中记录、接口健康检查等。这些内容通常由服务端持续产生,前端只需要追加展示、筛选、暂停滚动或复制。
这类场景里,SSE 比 WebSocket 更克制。因为浏览器端并不需要在同一条连接里高频发送消息,它只是订阅某个任务或某个资源的状态变化。
但日志场景也不能只做“来了就追加”。至少要考虑几件事:
- 日志是否有
sequence,用于排序和断线续传。 - 页面是否限制最大日志行数,避免内存无限增长。
- 用户暂停滚动时,新日志是否仍然接收但不强制滚到底。
- 断线恢复后,是否会重复显示已经看过的日志。
- 服务端是否发送心跳,避免代理或网关把空闲连接断掉。
日志流最容易被低估的地方是资源控制。一个看起来很简单的日志面板,如果没有行数上限、没有连接释放、没有断线策略,很容易从“好用的小工具”变成“慢慢拖垮页面的长期连接”。
所以,日志类 SSE 要同时关注两件事:事件顺序和资源生命周期。
场景四:轻量通知与状态变更
站内通知、审批提醒、订单状态变化、库存变动、支付状态回调,也可以使用 SSE。
这些场景不一定要求毫秒级实时,但希望用户不用刷新页面就能看到状态变化。相比轮询,SSE 可以减少无意义请求;相比 WebSocket,SSE 的连接模型和服务端实现通常更简单。
不过,通知类场景要小心一个边界:不是所有通知都适合每个用户维护一条长期连接。
如果系统用户量很大,而通知频率很低,直接为每个在线用户建立 SSE 连接可能并不划算。这个时候可以结合业务优先级做取舍:
- 高频工作台、运营后台、审批中心:SSE 更有价值。
- 普通低频消息提醒:轮询或页面聚焦时刷新可能已经够用。
- 需要跨端强一致提醒:还要结合服务端消息存储、已读状态和离线补偿。
通知场景里,SSE 只解决“在线时把变化推过来”。它不解决消息持久化、权限过滤、已读未读、多端同步这些业务问题。
这也是选择 SSE 时很重要的一条原则:不要把传输机制误认为完整业务系统。
场景五:仪表盘与只读实时数据
运营看板、库存看板、告警面板、订单大盘、设备状态面板,也经常会想到 SSE。
这类页面的共同特点是读多写少。用户主要观察数据变化,不一定需要频繁向服务端发送操作。服务端把最新指标、告警、状态变更推给前端,前端更新图表或列表。
但看板类场景要避免一个误区:不是所有数据点都要逐条推送。
如果指标变化频率很高,前端没有必要接收每一次原始变化。更合理的做法通常是服务端按窗口聚合后推送,比如每 1 秒或 3 秒推一次当前快照:
event: snapshot
data: {"onlineDevices":1280,"failedJobs":6,"queueSize":42}
对于仪表盘,用户真正需要的是稳定、可读、不会抖动的状态,而不是每一个底层事件。
所以看板类 SSE 的重点是快照设计,而不是事件数量。推得越多,不一定越实时;推得过多,反而会让页面渲染、网络和用户注意力都被噪音消耗。
SSE 不适合哪些业务场景
理解适合的场景,也要理解不适合的场景。
如果业务需要客户端和服务端在同一条连接上高频双向通信,SSE 通常不是首选。比如在线协同编辑、多人游戏、实时语音控制、复杂远程控制台,这些场景更接近 WebSocket 的能力边界。
如果业务需要上传流式请求体、复杂自定义请求头、精细控制重连退避或二进制帧,原生 EventSource 也会受限。浏览器原生 EventSource 不能像 fetch 那样灵活设置请求头,这会影响某些基于 Authorization 请求头的鉴权设计。
如果业务只是偶尔刷新一次状态,轮询可能更简单。工程判断不是“能用 SSE 就用 SSE”,而是看它是否真的降低了系统复杂度。
可以用下面这组问题判断:
- 服务端是否会持续产生对用户有价值的状态变化?
- 客户端是否主要接收,而不是持续发送?
- 用户是否需要看到中间过程,而不是只关心最终结果?
- 断线后是否需要恢复、去重或补偿?
- 系统是否能承受长期连接的资源成本?
如果这些问题大多是肯定答案,SSE 才值得进入方案设计。
真正落地时要补的工程能力
SSE 的 API 很简单,但业务落地不简单。
一个可靠的 SSE 方案,至少要补齐这些能力:
| 能力 | 为什么重要 |
|---|---|
| 事件类型 | 区分 chunk、progress、done、error、cancelled |
| 业务 ID | 确认事件属于哪个用户、会话、任务或消息 |
| 序号字段 | 支持排序、缺口检测、断线续传 |
| 去重规则 | 防止重连或重放导致 UI 重复渲染 |
| 完成事件 | 收到完成后主动关闭连接,避免继续重连 |
| 错误分类 | 区分网络错误、鉴权失败、任务不存在、不可恢复失败 |
| 心跳机制 | 防止代理或网关误判空闲连接 |
| 资源释放 | 页面离开、任务取消、服务端断开时释放连接和任务 |
| 降级策略 | 无法续传时拉快照、标记 partial 或允许重新生成 |
这张表背后的意思是:SSE 只是通道,可靠性来自协议、状态机和业务合约。
只写一个 new EventSource(url),最多只能证明浏览器能收到事件。要让它进入真实业务,需要回答更深的问题:
这个事件属于谁?
它是第几个?
我是否已经处理过?
它能不能直接展示?
它表示任务完成、失败,还是还在继续?
断线后我从哪里恢复?
如果无法恢复,用户应该看到什么?
结尾:SSE 的价值是克制
SSE 最有价值的地方,不是它比 WebSocket 更强,而是它在很多业务里刚刚好。
它比轮询更及时,也更少浪费。
它比 WebSocket 更简单,也更符合 HTTP 下的单向播报场景。
它适合 AI 输出、任务进度、日志监控、轻量通知和只读看板,因为这些场景都有一个共同结构:
用户发起或订阅一个目标,服务端持续产生状态,前端负责接收、展示、恢复和收尾。
但 SSE 不能替你设计业务协议,不能自动保证消息完整,也不能替代去重、状态机、快照和降级策略。
所以,选择 SSE 时最好的判断不是“这个功能要不要实时”,而是:
这个业务是不是需要服务端持续、单向、可恢复地告诉前端发生了什么?
如果答案是是,SSE 就不是一个炫技选项,而是一个很朴素、很合适的工程选择。