文章摘要:
在现代 Web 应用中,实时通信已成为标配。当需要从服务器向客户端推送消息时,Server-Sent Events 和 WebSocket 是两种最常用的技术。本文将从零开始介绍如何使用 SSE 构建一个简单的消息推送应用,并深入对比 SSE 与 WebSocket 在协议特性、使用场景、优缺点等方面的异同,助你在项目中做出最佳选择。
文章大纲
一、 引言:为什么需要服务器推送?
- 传统 Web 通信的瓶颈:经典的“请求-响应”模型(如 AJAX 轮询、长轮询)的缺点:延迟高、服务器压力大、资源浪费。
- 实时应用的兴起:在线聊天、实时数据监控(股票、体育比分)、消息通知、协同编辑等场景对低延迟、双向/单向通信的需求。
- 两大主流技术:HTML5 提供的两种解决方案——SSE 和 WebSocket。
二、 深入浅出 Server-Sent Events
-
SSE 是什么?
-
是一种基于 HTTP 协议的单向实时通信技术,允许服务器通过持久连接单向地向客户端推送事件,无需客户端持续请求。
包括:
- 服务器与客户端基于http的通信协议
- EventSource API
-
核心特性与优势
- 单向通信:仅支持服务器到客户端的数据流。
- 轻量:基于纯文本和 HTTP/HTTPS协议,易于理解和调试。
- 持久连接:客户端建立一次连接后,该连接会保持开放,直到关闭,服务器可以通过这条连接随时发送数据。
- 自动重连:内置断线重连机制。
- 事件流:服务器以数据流(event stream)的形式发送事件,客户端通过监听这个流来接收数据。
- 原生支持:现代浏览器原生支持
EventSourceAPI。
-
工作原理
- 建立连接:通常情况下,客户端(如浏览器)发送http get请求到服务器来建立一个sse连接【具体代码】
- 服务器响应:服务器接收到请求,返回一个状态码为200的http响应,响应头包含
- Content-Type: text/event-stream(表明客户端期望接收SSE数据流量)
- Connection: keep-alive(HTTP/1.1、HTTP2 默认长连接)
- Cache-Control: no-cache
- 数据推送:服务器可以通过已建立的连接向客户端推送数据,每次推送的数据成为一个事件event,每个事件由一个或多个’\n\n’分隔的数据块组成,每个数据块都是一个行文本,可以包含:
- ’data’:数据行
- ’id’:指定事件ID
- ’event’:事件类型
- ’:’:注释行
- 客户端处理:客户端中使用 JavaScript 的 EventSource API 来监听服务器推送的数据,并进行相应的处理。
- 重连:如果连接断开,客户端会自动尝试重新连接,如果服务器在事件中指定了ID,重连时客户端会发送一个’Last-Event-ID’的HTTP头部信息到服务器,根据这个信息,服务器可以决定从哪个事件开始重新发送数据。
-
注意事项
- 异步处理:由于sse 基于长连接机制,数据推送可能会持续较长时间,为防止服务器线程被阻塞,建议使用异步方式处理sse 请求
- 超时处理:sse 连接可能会因网络终端,客户端关闭等原因超时,为了避免无效连接占据服务器资源,建议设置超时时间并处理超时情况。如利用SseEmiter对象的setTimeout()方法设定超时时间,onTimeout()方法处理超时逻辑
- 异常处理:在实际应用中,可能会遇到网络异常,数据推送失败等问题,可以使用SseEmitter 对象的completeWithError()将异常信息发送客户端,客户端通过eventSource.onerror事件处理
- 内存管理:使用SseEmitter时,需要特别注意内存管理问题,尤其在大量并发连接的场景下。当客户端断开连接后,务必及时释放SseEmitter对象,避免资源泄漏和内存溢出
- 并发性能:sse的并发连接数可能对服务器性能产生影响,如需处理大量并发连接,可以考虑使用线程池或其他异步处理方式,以最大化服务器资源利用
- 客户端兼容性:旧版本的浏览器可能不支持,需要确保目标客户端对其良好的支持,或提供备选的实时数据推送机制
-
动手构建:一个简单的 SSE 应用
-
客户端实现
- 使用
EventSource对象连接到服务器端点。 - 监听
message事件、自定义事件。 - 处理
onopen,onerror事件。
- 使用
-
服务器端实现
-
设置必要的 HTTP 头:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive
-
数据格式规范:遵循
data:,event:,id:,retry:的格式。
-
-
-
SSE 的局限性
- 单向通信:客户端无法通过 SSE 连接向服务器发送数据(需额外使用 AJAX/Fetch)。
- 同源策略限制:默认只能连接同源服务器(可通过 CORS 解决)。
- 协议限制:仅支持 UTF-8 编码,无法传输二进制数据。
- 最大连接数:浏览器对同一域名的 HTTP 连接数有限制(通常为 6个)。
观察 DeepSeek 这类的 LLM 问答系统可以发现,实际上是用 fetch 发起 post 请求,这样浏览器发起请求时就可以带上用户的问题。demo
三、 WebSocket 技术简介
-
WebSocket 是什么?
- 基于 TCP 的独立协议,在客户端和服务器之间提供全双工、双向的通信通道。
-
核心特性与优势
- 真正的双向通信:客户端和服务器可以随时、独立地向对方发送消息。
- 低延迟:建立连接后,数据包开销极小。
- 支持二进制数据:可以高效传输 Blob、ArrayBuffer 等二进制数据。
-
WebSocket 的工作流程
- 通过 HTTP
Upgrade请求握手,升级到 WebSocket 协议。 - 握手成功后,通信完全在 WebSocket 协议上进行,脱离 HTTP。
- 通过 HTTP
四、 SSE 与 WebSocket 的全面对比
为了更直观,可以设计一个对比表格:
| 特性维度 | Server-Sent Events | WebSocket |
|---|---|---|
| 通信模式 | 单向 (服务器 -> 客户端) | 全双工双向 (服务器 <=> 客户端) |
| 底层协议 | HTTP | 独立的 ws/wss 协议 (基于TCP) |
| 协议开销 | 每个消息带有 HTTP 头(轻量) | 连接建立后,数据帧开销非常小(极轻) |
| 数据格式 | 仅文本 (UTF-8) | 文本和二进制数据 |
| 浏览器支持 | 广泛支持 (除 IE/Edge 旧版) | 广泛支持 (包括旧版IE10+) |
| 开发复杂度 | 简单,使用标准 HTTP | 相对复杂,需处理协议、心跳等 |
| 自动重连 | 原生支持 | 需要手动实现 |
| 适用场景 | 实时通知、新闻推送、状态更新、日志流 | 在线游戏、聊天室、协同编辑、实时交易 |
五、 如何选择:SSE 还是 WebSocket?
-
选择 SSE 的场景
-
你需要从服务器到客户端的单向数据流。例如:
- 实时通知:如系统通知、新消息提醒
- 动态更新:股票行情、体育比分、新闻更新等
- 日志和监控:实时查看日志,监控仪表盘和数据可视化
- 类似 chatGPT 的输出:逐字显示的 AI 生成文本
-
优势:实现简单、利用现有 HTTP 基础设施、自动重连。
-
-
选择 WebSocket 的场景
-
你需要真正的、低延迟的双向交互。例如:
- 多人在线游戏。
- 聊天应用程序。
- 带有实时协作功能的文档编辑器。
- 需要传输二进制数据的实时应用(如视频流控制)。
-
优势:极致的性能、完整的双向通信能力。
-
-
一种混合模式
- 场景:一个应用的不同部分有不同的需求。
- 示例:一个社交网站使用 SSE 接收新消息和通知,但在用户进入聊天室时,使用 WebSocket 进行双向聊天。
六、 总结与最佳实践
-
核心总结:
- SSE 是单向服务器推送的简单、高效的解决方案。
- WebSocket 是双向、低延迟实时通信的强大、复杂的解决方案。
-
选型建议:
- 先明确你的需求:是只需要接收,还是需要频繁地双向发送?
- 从简原则:如果你的场景只是服务器向客户端推送数据,SSE 通常是更简单、更好的选择。
- 不要过度工程化:不要为了使用 WebSocket 而使用 WebSocket,SSE 在它擅长的领域非常出色。