什么是 SSE?
在 AI 应用中提到的 SSE 通常指的是 Server-Sent Events (服务器发送事件),它是一种基于 HTTP 的简单技术,允许服务器主动向客户端(通常是 Web 浏览器)推送数据更新。它建立了一个持久的、单向(从服务器到客户端)的连接通道。
发送事件的服务器端脚本需要使用 MIME 类型 text/event-stream
进行响应。每个通知都以一对换行符终止的文本块的形式发送。事件流是一个简单的文本数据流,必须使用 UTF-8 进行编码。事件流中的消息由一对换行符分隔。冒号作为一行的第一个字符本质上是一个注释会被忽略。每条消息都包含一行或多行文本,其中列出了该消息的字段。每个字段都由字段名称表示,后跟冒号,后跟该字段值的文本数据。
事件流消息格式中的字段有:
- event 标识所描述的事件的类型
- data 消息的数据字段
- id 用于设置EventSource对象的最后一个事件ID值的事件ID
- retry 重连时间 所有其他字段名称都将被忽略。并不是每次消息流都需要这些字段,根据实际需要使用合适字段即可。
列举几个事件流消息示例:
- 只有数据的消息 下面示例:第一条为注释消息,第二条为一行数据的消息,第三条为两行数据的消息
kotlin
: this is a test stream
data: some text
data: another message
data: with two lines
- 具有类型的消息 下面消息中每条都是有具体类型的消息,消息数据是 json 串
vbnet
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}
event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}
event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}
- 混合型消息 下面示例消息混合不同字段的消息,包含事件类型和没有事件类型的消息,消息数据格式也可以根据情况不一样
vbnet
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}
data: Here's a system message of some kind that will get used
data: to accomplish some task.
event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
为什么 AI 应用会使用 SSE?
AI 应用,尤其是那些涉及大型语言模型、实时推理、流式输出或长时间运行任务的场景,非常适合且经常使用 SSE。主要原因如下:
-
实时流式传输:
- 这是 SSE 的核心优势。对于像 ChatGPT 这样的聊天机器人或文本生成模型,响应不是一次性返回的,而是逐词、逐句生成的。
- SSE 允许服务器将每个生成的词或句子片段实时推送到用户的浏览器界面 ,用户可以看到文字像打字一样逐个出现,极大地提升了交互的流畅性和响应感。
- 传统 AJAX 轮询或等待整个响应完成再返回的方式会带来明显的延迟感,用户体验较差。
-
处理长时间运行的任务:
- 某些 AI 任务(如复杂的图像生成、视频处理、大型数据集分析)可能需要较长时间(几秒甚至几分钟)。
- SSE 可以用于在任务执行过程中持续向客户端发送进度更新(例如,"处理中:25%"、"处理中:50%"),让用户知道任务仍在进行中,而不是面对一个空白的加载界面或担心请求是否超时失败。
-
减少延迟感知:
- 即使整个响应时间相同,看到内容逐步出现(流式传输)比等待所有内容一次性加载完毕感觉上更快,用户心理上更舒适。
-
简化实现(相比 WebSocket):
- SSE 基于标准的 HTTP/HTTPS 协议,实现相对简单。服务器端只需要设置正确的 HTTP 头(
Content-Type: text/event-stream
)并保持连接打开,持续发送特定格式的数据。 - 客户端(浏览器)使用内置的
EventSource
API 即可轻松接收和处理事件,无需复杂的握手或协议管理。 - 虽然 WebSocket 功能更强大(全双工),但如果应用场景只需要服务器向客户端推送数据(这正是大多数 AI 响应场景的核心需求),SSE 是更轻量级、更简单的选择。
- SSE 基于标准的 HTTP/HTTPS 协议,实现相对简单。服务器端只需要设置正确的 HTTP 头(
-
自动重连:
EventSource
API 内置了连接断开后的自动重连机制,提高了连接的健壮性。
SSE 在 AI 应用中的典型工作流程
-
用户发起请求: 用户在 AI 应用界面输入提示或上传数据,点击提交。
-
建立 SSE 连接: 客户端(浏览器)使用 JavaScript 的
EventSource
对象,向服务器的一个特定端点(URL)发起连接请求。这个请求头包含Accept: text/event-stream
。 -
服务器响应并保持连接: 服务器识别到这是一个 SSE 请求:
- 设置响应头
Content-Type: text/event-stream
。 - 保持这个 HTTP 连接处于打开状态(不关闭)。
- 设置响应头
-
AI 处理与流式推送:
- 服务器端开始执行 AI 模型推理(生成文本、处理任务等)。
- 关键步骤: 每当模型生成一部分结果(一个词、一个句子片段、一个进度百分比),服务器就将这部分数据格式化 (遵循
data: <content>\n\n
等简单格式)并通过这个保持打开的连接立即发送给客户端。
-
客户端实时接收并渲染:
- 客户端的
EventSource
监听'message'
事件。 - 每当服务器推送一条新的数据,
EventSource
就会触发事件。 - 客户端 JavaScript 解析接收到的数据片段,并实时更新页面内容(将新词添加到聊天窗口、更新进度条等)。
- 客户端的
-
任务完成或连接关闭:
- 当 AI 任务完全结束,服务器发送一个明确的结束信号(可选,可通过自定义事件实现)或直接关闭连接。
- 客户端收到连接关闭事件,进行必要的清理或状态更新。
SSE 的局限性(以及与 WebSocket 的对比)
- 单向通信: SSE 仅支持服务器到客户端的推送。如果应用需要客户端在同一个连接上频繁地向服务器发送大量数据(如实时协作编辑、在线游戏),WebSocket 是更好的选择(全双工)。
- 连接限制: 浏览器通常对同一个域名下的并发 HTTP 连接数有限制(包括 SSE 连接)。WebSocket 连接不受此限制。
- 协议: 严格基于 HTTP(S)。
- 数据格式: 消息是文本格式(虽然可以发送 JSON 字符串)。WebSocket 可以处理二进制数据。
- 老浏览器兼容性: 绝大多数现代浏览器都支持 SSE,但极老的浏览器(如 IE)不支持。
总结
在 AI 应用中,SSE (Server-Sent Events) 是一种高效、简单且标准化的技术,用于实现服务器向 Web 客户端实时推送数据流。它完美契合了 AI 响应需要流式生成(逐词输出)或长时间任务需要进度反馈的核心需求,显著提升了用户体验的流畅性和响应速度。当你的 AI 应用主要需要服务器主动推送更新给浏览器,而不需要双向高频通信时,SSE 通常是比 WebSocket 更轻量、更易实现的选择。
参考资料
1\] [html.spec.whatwg.org/multipage/s...](https://link.juejin.cn?target=https%3A%2F%2Fhtml.spec.whatwg.org%2Fmultipage%2Fserver-sent-events.html "https://html.spec.whatwg.org/multipage/server-sent-events.html") \[2\] [developer.mozilla.org/en-US/docs/...](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FServer-sent_events%2FUsing_server-sent_events "https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events") \[3\] [en.wikipedia.org/wiki/Server...](https://link.juejin.cn?target=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FServer-sent_events "https://en.wikipedia.org/wiki/Server-sent_events")