欢迎光临
我们一直在努力

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:无需协议升级,天然穿透防火墙/代理
  • 自动重连:浏览器 EventSource API 内置断线重连机制
  • 纯文本传输:数据格式为 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)

const source = new EventSource('/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');

let count = 0;
const timer = 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 流式输出常用)

from fastapi.responses import StreamingResponse
import asyncio, json

async def event_generator():
for i in range(10):
yield f"data: {json.dumps({'token': f'word_{i}'})}\\n\\n"
await asyncio.sleep(0.5)
yield "data: [DONE]\\n\\n"

@app.get("/chat/stream")
async def chat_stream():
return StreamingResponse(
event_generator(),
media_type="text/event-stream"
)

五、 SSE vs WebSocket 选型指南

维度SSEWebSocket
通信方向 单向(服务器→客户端) 双向
协议 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
  • 认证问题:EventSource API 不支持自定义 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流式输出 #前端 #后端

    如果这篇笔记对你有帮助,欢迎点赞收藏 ❤️ ~

    赞(0)
    未经允许不得转载:171主机测评 » SSE (Server-Sent Events) 详解:比 WebSocket 更轻量的实时推送方案
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址