Server-Sent Events 是什么?它和 WebSocket、普通轮询有什么区别?
简化版
Server-Sent Events,简称 SSE,是基于 HTTP 的服务端单向推送机制。
客户端通过 EventSource 建立一个长连接,服务端用 text/event-stream 持续向客户端发送事件流。它适合通知、进度、消息流、监控面板等“服务端不断推给客户端”的场景。
和 WebSocket 相比,SSE 是单向推送、基于 HTTP、实现简单、天然支持断线重连;WebSocket 是全双工通信,更适合双向实时交互。
详细版
SSE 响应头通常是:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
事件格式类似:
event: message
id: 101
data: hello
浏览器端:
const es = new EventSource("/events");
es.onmessage = (event) => console.log(event.data);
SSE 只能服务端推客户端,客户端要发消息仍然走普通 HTTP 请求。
它比轮询节省请求开销,比 WebSocket 简单,但不适合强双向互动和二进制传输。
完整版教学
1. 先给出 SSE 的定位
SSE 是浏览器和服务端之间的一条 HTTP 长连接。
连接建立后,服务端可以不断往客户端写事件。
客户端只负责接收。
这和“客户端不断问服务端有没有新数据”的轮询不同。
SSE 的关键词是:HTTP、长连接、服务端单向推送、文本事件流。
2. SSE 的响应格式
服务端响应头通常包含:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
响应体是一段持续输出的文本流。
每个事件由若干字段组成,并用空行分隔。
id: 1
event: progress
data: {"percent":30}
id: 2
event: progress
data: {"percent":60}
客户端收到空行后认为一个事件结束。
3. 浏览器如何使用
浏览器原生提供 EventSource。
const source = new EventSource("/api/events");
source.addEventListener("progress", (event) => {
console.log(event.data);
});
source.onerror = () => {
console.log("connection error");
};
如果连接断开,浏览器会自动尝试重连。
服务端可以通过 id 配合 Last-Event-ID 帮助客户端从断点继续。
4. 和轮询、长轮询、WebSocket 对比
| 方案 | 通信方向 | 协议基础 | 优点 | 适合场景 |
|---|---|---|---|---|
| 短轮询 | 客户端反复请求 | HTTP | 简单 | 低频状态刷新 |
| 长轮询 | 请求挂起等待结果 | HTTP | 兼容性好 | 中低频通知 |
| SSE | 服务端持续推送 | HTTP | 简单、自动重连 | 消息流、进度 |
| WebSocket | 双向通信 | 独立升级协议 | 全双工 | 聊天、游戏、协作 |
SSE 不需要协议升级,部署上通常比 WebSocket 更接近普通 HTTP。
5. SSE 的限制
SSE 不是万能实时方案。
它有一些限制:
- 主要传文本,不适合直接传二进制。
- 通信方向是服务端到客户端。
- 部分代理可能缓冲响应,导致事件不能及时到达。
- 浏览器对同域连接数有限制。
- 长连接会占用服务端连接资源。
如果业务需要客户端高频发送消息,WebSocket 更合适。
6. 工程实践注意点
实现 SSE 时要注意:
- 关闭代理缓冲,例如 Nginx 的 buffering。
- 定期发送心跳,避免连接被中间设备断开。
- 设置合理的重试间隔。
- 每个事件带 id,便于断线恢复。
- 服务端使用异步 I/O 或事件驱动模型承载大量连接。
- 不要让响应被普通缓存缓存。
一个心跳事件可以很简单:
: ping
冒号开头是注释行,客户端可忽略,但能保持连接活跃。
7. 常见误区与追问
- 误区:SSE 和 WebSocket 一样是双向通信。 SSE 主要是服务端到客户端的单向推送。
- 误区:SSE 只是普通轮询。 SSE 是一次长连接持续接收事件,不是反复建立请求。
- 误区:SSE 不需要考虑连接资源。 长连接会占用连接和内存,需要服务端模型支持。
- 追问:SSE 为什么适合进度条? 服务端可以持续推送当前进度,客户端实时更新。
- 追问:断线后怎么恢复? 使用事件 id 和
Last-Event-ID,服务端按需补发。 - 追问:为什么代理缓冲会影响 SSE? 代理如果攒够数据再转发,客户端就无法实时收到事件。
8. 加强记忆
SSE 记成“HTTP 上的服务端喇叭”:服务端一直说,客户端一直听。
如果题目问实时通信选择,先判断单向还是双向,再决定 SSE 或 WebSocket。