SSE常用业务场景:从实时通知到AI流式输出

1 阅读10分钟

很多人第一次接触 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 方案,至少要补齐这些能力:

能力为什么重要
事件类型区分 chunkprogressdoneerrorcancelled
业务 ID确认事件属于哪个用户、会话、任务或消息
序号字段支持排序、缺口检测、断线续传
去重规则防止重连或重放导致 UI 重复渲染
完成事件收到完成后主动关闭连接,避免继续重连
错误分类区分网络错误、鉴权失败、任务不存在、不可恢复失败
心跳机制防止代理或网关误判空闲连接
资源释放页面离开、任务取消、服务端断开时释放连接和任务
降级策略无法续传时拉快照、标记 partial 或允许重新生成

这张表背后的意思是:SSE 只是通道,可靠性来自协议、状态机和业务合约。

只写一个 new EventSource(url),最多只能证明浏览器能收到事件。要让它进入真实业务,需要回答更深的问题:

这个事件属于谁?
它是第几个?
我是否已经处理过?
它能不能直接展示?
它表示任务完成、失败,还是还在继续?
断线后我从哪里恢复?
如果无法恢复,用户应该看到什么?

结尾:SSE 的价值是克制

SSE 最有价值的地方,不是它比 WebSocket 更强,而是它在很多业务里刚刚好。

它比轮询更及时,也更少浪费。

它比 WebSocket 更简单,也更符合 HTTP 下的单向播报场景。

它适合 AI 输出、任务进度、日志监控、轻量通知和只读看板,因为这些场景都有一个共同结构:

用户发起或订阅一个目标,服务端持续产生状态,前端负责接收、展示、恢复和收尾。

但 SSE 不能替你设计业务协议,不能自动保证消息完整,也不能替代去重、状态机、快照和降级策略。

所以,选择 SSE 时最好的判断不是“这个功能要不要实时”,而是:

这个业务是不是需要服务端持续、单向、可恢复地告诉前端发生了什么?

如果答案是是,SSE 就不是一个炫技选项,而是一个很朴素、很合适的工程选择。