在 AI 技术快速迭代的背景下,LLM 聊天机器人从 2023 年的爆款产品,到 2024 年 AI 推理场景普及,再到 2025 年 AI Agent 成为主流,流式输出始终是影响用户体验的关键环节。
SSE(Server-Sent Events,服务器发送事件)作为轻量高效的流式实现方案,它不仅是前端工程师的核心职责所在,更成为 2025 年大厂 AI 相关岗位的必考题。
今天,我将从技术定位、全栈实现到核心原理,全面拆解 SSE 流式输出的落地逻辑。
一、SSE 是什么?
1.概念:
SSE 是 HTML5 标准定义的技术方案,它的核心作用是让服务器能主动、持续地向客户端推送流式数据,无需客户端频繁发起请求。
SSE 基于 HTTP 协议扩展而来,无需引入复杂的新协议,就能突破传统 HTTP “请求 - 响应后断开连接” 的限制,实现 “一次连接、多次推送”,尤其适合 LLM 聊天机器人、实时监控、消息通知等 “服务器单向生成数据” 的场景。
2.作用:
对前端而言,SSE 能让数据 “边生成边输出”,用户无需等待全量数据,就能更快看到响应。
对后端而言,SSE 契合 LLM 的生成逻辑 —— 大模型生成内容时,会以 “token” 为单位逐次输出,比如你问 “你是谁?” 后,ai会生成 “我”,然后再根据前文逐步推理后续内容,SSE 可直接将这一过程实时传递给客户端,无需等待所有 token 生成完毕。
二、前端实现:用 EventSource 接收 SSE 流式数据
前端是 SSE 流式输出的 “展示端”,它的核心职责是通过原生 API 接收服务器推送的数据,并实时渲染到页面,打造 “逐字弹出” 的流畅体验。以下是一段完整的前端代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Chatbot 流式输出</title>
</head>
<body>
<h1>流式输出</h1>
<div id="messages"></div>
<script>
// 1. 实例化EventSource,指定SSE服务端路由,建立连接
const source = new EventSource('/sse');
// 2. 监听SSE的message事件,接收服务器推送的数据
source.onmessage = function(event) {
console.log(event.data);
// 3. 实时渲染数据到页面:获取容器并追加内容
const messages = document.getElementById('messages');
messages.innerHTML += event.data;
}
</script>
</body>
</html>
前端代码解析
-
EventSource:SSE 的客户端核心 API
new EventSource('/sse')是建立 SSE 连接的关键 —— 它会向服务器/sse路由发起 GET 请求,并自动保持连接,监听服务器推送的事件。这一 API 是 HTML5 原生支持的,无需引入第三方库,兼容性覆盖所有现代浏览器(除 IE 外)。
-
onmessage 事件
服务器推送数据时,会触发onmessage事件,event.data即为推送的具体内容。代码中通过innerHTML += event.data将数据实时追加到messages容器,实现 “边接收边展示” 的效果。这种方式其实利用 “心理学障眼法”—— 用户看到内容逐次弹出时,会感觉 AI 在 “实时思考”,即使全部数据还未生成,也愿意花时间等待,从而大幅提升用户体验。
-
SSE 的连接特性
EventSource 会自动处理连接维护:若网络中断,会默认尝试重连。若服务器主动关闭连接,客户端也会感知并停止监听。这种 “免维护” 特性让前端无需额外编写重连逻辑,降低开发复杂度。
三、后端实现:用 Express 搭建 SSE 推送服务
后端是 SSE 流式输出的 “数据生成端”,核心职责是通过 HTTP 服务配置 SSE 响应规则,持续生成并推送数据:
// 1. 引入依赖:Express框架(处理路由)、HTTP模块(创建服务器)
const express = require('express');
const app = express();
const http = require('http').Server(app);
// 2. 静态页面路由:响应前端页面请求
app.get('/', (req, res) => {
console.log(__dirname);
// __dirname为Node.js内置变量,指向当前脚本所在目录,确保正确返回index.html
res.sendFile(__dirname + '/index.html');
});
// 3. SSE核心路由:配置服务器推送规则
app.get('/sse', (req, res) => {
// 3.1 配置SSE专属响应头,告知客户端按SSE规则处理数据
res.set({
'Content-Type': 'text/event-stream', // 声明响应为SSE事件流格式
'Cache-Control': 'no-cache', // 禁止前端缓存,确保每次接收新数据
'Connection': 'keep-alive' // 保持HTTP连接,不主动断开
});
res.flushHeaders(); // 刷新响应头,确保配置立即生效
// 3.2 模拟持续生成数据:每1秒推送一次当前时间
setInterval(() => {
const message = `Current Time is ${new Date().toLocaleTimeString()}`;
// 按SSE格式推送数据:以"data:"开头,双换行结尾(消息分隔符)
res.write(`data:${message}\n\n`);
}, 1000);
});
// 4. 启动HTTP服务器,监听1314端口
http.listen(1314, () => {
console.log('server is running on 1314');
});
后端代码解析
-
Express 路由设计:区分页面路由与 SSE 路由
-
app.get('/'):为前端提供静态页面,当用户访问http://localhost:1314时,返回 index.html,这是 B/S 架构的基础交互逻辑。 -
app.get('/sse'):SSE 的核心推送路由,专门处理客户端的 SSE 连接请求,配置响应头并持续推送数据。
-
-
SSE 响应头:突破传统 HTTP 限制的关键
传统 HTTP 协议为了支持高并发,会在返回数据后立即断开连接,而 SSE 通过以下响应头实现 “持续连接”:Content-Type: text/event-stream:声明响应类型为 SSE 事件流,客户端 EventSource 会基于此格式解析数据;Cache-Control: no-cache:禁止前端缓存数据,避免旧数据干扰实时性(尤其 LLM 回答是动态生成的,必须禁用缓存);Connection: keep-alive:强制保持 HTTP 连接,为持续推送奠定基础;res.flushHeaders():Node.js 环境中需手动刷新响应头,避免配置被缓冲区阻塞,确保客户端能立即识别 SSE 格式。
-
数据推送逻辑:setInterval 与 res.write
setInterval:模拟服务器持续生成数据的场景(如 LLM 逐 token 生成回答),代码中每 1 秒生成一次当前时间,实际项目中可替换为 “监听 LLM 的 token 生成事件”;res.write(data:${message}\n\n):按 SSE 标准格式推送数据 —— 必须以data:开头,以\n\n(双换行)结尾,这是客户端识别 “一条完整消息” 的关键,缺少双换行会导致数据拼接错误。
-
HTTP 模块的作用
通过const http = require('http').Server(app)为 Express 应用添加 HTTP 服务器能力,http.listen(1314)让服务在 1314 端口处于 “伺服状态”—— 持续等待客户端请求,符合服务器的核心定位。
四、SSE 的核心原理:基于 HTTP 的单向推送机制
SSE 能实现 “持续推送”,本质是对 HTTP 协议的巧妙扩展,核心逻辑可拆解为三个步骤:
-
建立连接:前端通过 EventSource 向服务器 SSE 路由发起 GET 请求,服务器返回配置好的 SSE 响应头,双方建立长期 HTTP 连接(由
Connection: keep-alive保障); -
持续推送:服务器有新数据时(如 LLM 生成新 token、实时时间更新),通过
res.write按 SSE 格式发送数据,数据会实时通过已建立的连接传输到前端; -
数据处理:前端 EventSource 监听
onmessage事件,收到数据后立即渲染,直到连接被主动关闭(如服务器完成数据生成、客户端关闭页面)。
这种机制既保留了 HTTP 协议的 “简单性”(无需复杂的双工通信),又满足了 “实时性” 需求,完美契合 LLM 聊天机器人等 “单向数据生成” 场景 —— 服务器只需推送回答,无需客户端持续发送请求,资源占用远低于传统轮询。
五、SSE 的价值与应用场景
1. 核心价值
- 提升用户体验:前端无需等待全量数据,数据边生成边展示,消除 “空白等待” 焦虑,尤其 LLM 生成长文本时,用户能快速看到初步响应;
- 降低技术成本:基于 HTTP 协议,无需引入 WebSocket 等复杂技术,前后端开发成本低,易落地;
- 节省资源:相比
setInterval轮询(前端频繁发起请求),SSE 只需一次连接,减少 90% 以上的无效请求,服务器能服务更多用户。
2. 典型应用场景
- LLM 聊天机器人:推送逐 token 生成的回答,实现 “打字机” 效果;
- 实时监控:推送服务器 CPU 使用率、IoT 设备传感器数据等;
- 消息通知:推送系统公告、用户消息等单向通知内容。
六、SSE 注意事项
- 请求方法限制:SSE 仅支持 GET 请求,客户端必须通过 GET 方式建立连接,后端路由需对应配置;
- 消息格式严格:数据必须以
data:开头、\n\n结尾,格式错误会导致客户端无法解析; - 连接数限制:浏览器对单个域名的 SSE 并发连接数有上限(通常为 6 个),避免同一页面创建过多 EventSource 实例;
- 兼容性:不支持 IE 浏览器,若需兼容,可降级为 “HTTP 分块传输” 方案。