SSE (Server-Sent Events) 详解:比 WebSocket 更轻量的实时推送方案
🚀 SSE (Server-Sent Events) 详解:比 WebSocket 更轻量的实时推送方案
摘要:在 AI 流式输出、实时通知、数据监控等场景中,我们常需要服务器主动向客户端推送数据。除了大名鼎鼎的 WebSocket,还有一个被严重低估的“神器”——SSE。本文将带你彻底搞懂 SSE 的原理、协议格式、代码实战以及与 WebSocket 的选型对比。
一、 什么是 SSE?
SSE(Server-Sent Events,服务端推送事件)是一种基于 HTTP 协议的单向实时通信技术。它是 HTML5 规范的一部分,允许服务器向客户端持续发送文本数据流。
用一句话概括它的本质:
SSE = HTTP 长连接 + 文本事件流格式 + 浏览器原生自动重连
💡 核心特点
- 单向通信:只能服务器 → 客户端(客户端发请求仍走普通 HTTP)
- 基于 HTTP:无需协议升级,天然穿透防火墙/代理
- 自动重连:浏览器
EventSourceAPI 内置断线重连机制 - 纯文本传输:数据格式为 UTF-8 文本,不支持二进制
- 轻量级:无需引入第三方库,浏览器原生支持
二、 为什么需要 SSE?(痛点分析)
在没有 SSE 之前,实现"服务器推数据"通常有两种方式:
| 方案 | 缺点 |
|---|---|
| 短轮询 (Polling) | 定时发请求,浪费带宽和 CPU,延迟高 |
| 长轮询 (Long Polling) | 每次收到数据后连接断开再重连,开销大 |
| WebSocket | 双向通信能力强,但协议复杂、需额外部署、部分代理不友好 |
SSE 恰好填补了中间地带:当你只需要"服务器持续推数据给客户端"时,它比轮询高效得多,又比 WebSocket 简单得多。
🔥典型场景:ChatGPT 等 AI 对话的流式输出、股票行情推送、构建进度条、实时日志、消息通知
三、 工作原理与协议格式
3.1 通信流程
客户端 服务器 | | |--- GET /events -------------->| | Accept: text/event-stream | | | |<-- 200 OK -------------------| | Content-Type: text/event-stream | Cache-Control: no-cache | | | |<-- data: {"msg":"hello"} ----| ← 第一条数据 |<-- data: {"msg":"world"} ----| ← 第二条数据 |<-- event: done --------------| ← 自定义事件 |<-- data: [DONE] -------------| | ...连接保持打开... |3.2 SSE 数据格式(重要!)
SSE 是纯文本协议,每条消息由以下字段组成(以\n\n分隔多条消息):
| 字段 | 必填 | 说明 |
|---|---|---|
data: | ✅ | 消息内容,多行数据每行都以data:开头 |
event: | ❌ | 事件名称,默认为message |
id: | ❌ | 消息ID,用于断线重连时告诉服务器上次收到的位置 |
retry: | ❌ | 重连等待时间(毫秒) |
示例原始响应体:
id: 1 event: update data: {"temperature": 26.5} data: 这是一条普通消息 event: done data: [DONE]四、 代码实战
4.1 前端(浏览器原生 API)
constsource=newEventSource('/api/stream');// 默认 message 事件source.onmessage=(e)=>{console.log('收到数据:',e.data);};// 自定义事件监听source.addEventListener('update',(e)=>{console.log('更新事件:',JSON.parse(e.data));});// 错误处理 & 自动重连source.onerror=(e)=>{console.log('连接异常,浏览器将自动重连...',e);// 如需手动关闭:source.close()};4.2 后端(Node.js / Express 示例)
app.get('/api/stream',(req,res)=>{// ⚠️ 关键响应头res.setHeader('Content-Type','text/event-stream');res.setHeader('Cache-Control','no-cache');res.setHeader('Connection','keep-alive');letcount=0;consttimer=setInterval(()=>{res.write(`data:${JSON.stringify({count:++count})}\n\n`);if(count>=10){clearInterval(timer);res.write('event: done\ndata: [DONE]\n\n');res.end();}},1000);req.on('close',()=>clearInterval(timer));});4.3 Python FastAPI 示例(AI 流式输出常用)
fromfastapi.responsesimportStreamingResponseimportasyncio,jsonasyncdefevent_generator():foriinrange(10):yieldf"data:{json.dumps({'token':f'word_{i}'})}\n\n"awaitasyncio.sleep(0.5)yield"data: [DONE]\n\n"@app.get("/chat/stream")asyncdefchat_stream():returnStreamingResponse(event_generator(),media_type="text/event-stream")五、 SSE vs WebSocket 选型指南
| 维度 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器→客户端) | 双向 |
| 协议 | HTTP | WS/WSS |
| 数据格式 | 纯文本 | 文本 + 二进制 |
| 自动重连 | ✅ 浏览器内置 | ❌ 需手动实现 |
| 实现复杂度 | ⭐ 极低 | ⭐⭐⭐ 较高 |
| 代理/防火墙兼容 | ✅ 优秀 | ⚠️ 可能被拦截 |
| 连接数限制 | 同域最多 6 个(HTTP/1.1) | 无此限制 |
| 适用场景 | AI流式输出、通知、监控 | 聊天室、游戏、协同编辑 |
⚠️注意:HTTP/1.1 下浏览器对同一域名 SSE 连接数限制为 6 个。如果使用 HTTP/2+,此问题基本消除。生产环境强烈建议开启 HTTP/2。
六、 常见坑与最佳实践
- Nginx 缓冲问题:Nginx 默认会缓冲响应,导致 SSE 数据延迟到达。务必添加:
proxy_buffering off; proxy_cache off; X-Accel-Buffering: no; # 或在后端响应头中设置 - 跨域问题:SSE 遵循 CORS 规则,需正确配置
Access-Control-Allow-Origin - 认证问题:
EventSourceAPI不支持自定义 Header,无法传 Token。解决方案:- URL 参数传递 Token(注意安全)
- Cookie 认证
- 使用
fetch+ReadableStream手动实现 SSE 客户端(推荐)
- 断点续传:善用
id字段 +Last-Event-ID请求头,实现断线后从断点继续接收 - 不要滥用:如果需要频繁双向通信,请直接上 WebSocket
七、 总结
| 你的需求 | 推荐方案 |
|---|---|
| 服务器单向推数据,快速落地 | ✅SSE |
| AI 大模型流式对话 | ✅SSE(业界标准) |
| 实时聊天 / 多人协作 / 游戏 | WebSocket |
| 低频数据同步 | 短轮询 / 长轮询 |
SSE 不是 WebSocket 的替代品,而是互补品。在合适的场景选择合适的工具,才是架构设计的精髓。
📝笔记标签:
#SSE#Server-Sent-Events#实时通信#WebSocket#AI流式输出#前端#后端如果这篇笔记对你有帮助,欢迎点赞收藏 ❤️ ~
