AI时代,为什么大模型都在用SSE?
随着大模型应用的爆发式增长,SSE从一个相对小众的HTML5规范,一跃成为AI应用中最主流的实时通信协议。今天,我们就来把SSE彻底讲清楚。
一、SSE到底是什么?
一句话概括:SSE是一种基于HTTP协议的服务器推送技术,允许服务端主动向客户端持续发送数据流。
你可以把它理解为服务器打开了一条“单向数据管道”。连接建立后,服务器可以源源不断地把数据“灌”给浏览器,而不需要客户端反复发请求来问“有新数据吗”。
它的本质其实很简单:SSE = HTTP扩展字段 + Keepalive长连接。
与WebSocket不同,SSE不需要额外的协议升级握手,它直接跑在标准的HTTP协议之上,是HTML5规范的一部分。目前几乎所有主流浏览器都原生支持SSE,客户端只需一个EventSource对象就能发起连接。
工作流程大致是这样的:
客户端发起连接:浏览器通过
EventSource向服务器发起一个HTTP GET请求服务器保持连接:服务器收到请求后不关闭连接,而是保持响应打开
持续推送数据:服务器以特定格式持续发送数据,每条消息以
data:开头,以两个换行符\n\n结尾客户端接收处理:浏览器监听事件,实时处理收到的数据
自动重连:如果连接断开,浏览器会自动尝试重新连接
服务器端只需要设置Content-Type: text/event-stream响应头,就可以开始推送数据了。一个典型的数据格式是这样的:
data: {"chunk": "这是第一部分内容", "id": 1}
data: {"chunk": "这是第二部分内容", "id": 2}除了data字段,SSE还支持event(定义事件类型)、id(标识事件,用于断线恢复)、retry(指定重连等待时间)等字段,设计相当精巧。
二、为什么AI大模型选了SSE?
这是很多人好奇的问题。实时通信方案那么多,WebSocket不是更强大吗?为什么ChatGPT、Claude、文心一言这些AI产品都选了SSE?
核心原因:AI对话的场景天然就是“单向推送”。
你问一个问题,AI一个字一个字地把答案“说”出来。你不需要在AI回答的过程中再插话——AI也不需要中途听你说话。这个场景下,WebSocket的双向通信能力完全是多余的。
打个比方:WebSocket像微信语音通话,你和对方随时都能说话;SSE像新闻推送,只有服务器能“说话”,你只管听。
具体来说,SSE在AI场景中的优势体现在几个方面:
架构更简单,生态更友好。 WebSocket需要一次额外的协议升级握手(Upgrade: websocket),对CDN、防火墙、代理服务器的兼容性不如普通HTTP。而SSE天然融入HTTP生态,能无缝通过反向代理、负载均衡器,无需特殊配置。这对于需要部署在复杂网络环境中的AI应用来说,是个巨大的优势。
开发成本更低。 WebSocket需要专门的服务端库和客户端处理逻辑,还要自己实现心跳、重连、帧解析等机制。SSE的客户端用浏览器原生EventSource API就行,服务端也就是写一个不关闭的HTTP响应。
性能完全够用。 一项针对真实数据流的测试表明,SSE和WebSocket在每秒事件数方面表现相当,WebSocket仅在高并发下略有优势。但对于AI对话这种以文本 token 为粒度的场景,SSE的性能绰绰有余。
正因如此,OpenAI的SDK、Anthropic的SDK、Vercel AI SDK,默认的流式传输协议都是SSE。可以说,SSE已经成为AI应用流式输出的“事实标准”。
还有一个重要变化值得一提:早期SSE受限于HTTP/1.1下浏览器“每个域名最多6个连接”的限制,多标签页场景下体验不好。但随着HTTP/2的普及,连接多路复用彻底解决了这个问题,SSE的最后一块短板也被补齐了。
三、SSE、WebSocket、轮询怎么选?
快速对比一下四种主流方案:
| 特性 | 短轮询 | 长轮询 | SSE | WebSocket |
|---|---|---|---|---|
| 通信方向 | 客户端→服务器 | 客户端→服务器 | 服务器→客户端 | 双向 |
| 协议 | HTTP | HTTP | HTTP | 独立协议 |
| 数据格式 | 文本/二进制 | 文本/二进制 | 文本 | 文本/二进制 |
| 自动重连 | 无 | 无 | 内置 | 需自行实现 |
| 开发复杂度 | 低 | 中 | 低 | 高 |
| 典型场景 | 简单数据刷新 | 消息通知 | AI流式输出、实时通知 | 聊天室、游戏、协同编辑 |
选型的原则很简单:
只需要服务器推数据给客户端 → 用SSE,简单够用
需要双向实时交互 → 用WebSocket
数据更新频率低、实时性要求不高 → 轮询就够
四、动手实战:从零实现一个SSE接口
理论讲完了,来看代码。先从后端开始。
后端实现(Node.js + Express):
app.get('/api/ai/stream', (req, res) => {
// 设置SSE必需的响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// 模拟AI逐字生成
const answer = ['你好', ',我是', '一个AI', '助手。'];
let i = 0;
const timer = setInterval(() => {
if (i < answer.length) {
res.write(`data: ${JSON.stringify({ chunk: answer[i] })}\n\n`);
i++;
} else {
res.write('data: {"type":"complete"}\n\n');
clearInterval(timer);
res.end();
}
}, 500);
});这里的关键点:设置正确的响应头、用res.write()持续写入数据、每条消息以\n\n结尾。
前端接收(浏览器原生EventSource):
const eventSource = new EventSource('/api/ai/stream');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'complete') {
eventSource.close();
return;
}
// 逐字追加到页面,实现"打字机"效果
document.getElementById('output').innerText += data.chunk;
};
eventSource.onerror = () => {
console.log('连接中断,浏览器将自动重连...');
};就这么简单。不需要引入任何第三方库,浏览器原生支持。
五、生产环境必须注意的几件事
把SSE跑起来不难,但要在生产环境中稳定运行,有几个坑必须提前避开。
心跳保活。 长时间没有数据传输时,中间的代理服务器(如Nginx)可能会主动断开连接。解决方案是服务端定时发送心跳包——格式可以是data: \n\n或注释行: heartbeat\n\n,建议间隔30秒,小于代理的闲置超时时间即可。
代理缓冲问题。 Nginx默认会缓冲响应内容,这会导致SSE数据不能实时到达客户端。需要在配置中禁用缓冲,并设置X-Accel-Buffering: no响应头。
跨域配置。 SSE受同源策略限制,跨域场景需要正确配置CORS。注意:如果前端设置了withCredentials: true,Access-Control-Allow-Origin不能设为*,必须指定具体域名。
高并发优化。 当并发用户量大时,单用户多轮对话独占长连接会导致连接数急剧膨胀。可以通过会话绑定和连接复用,让单条连接承载多个会话,实测可将并发连接数降低60%,资源占用下降45%。
chunk大小调优。 服务端推送的数据块不宜过小(如10个token),否则协议开销过大;也不宜过大(如512个token),否则实时性下降。建议根据模型生成速度动态调整,主流方案采用64-128 token/块。