不喜欢广告? 无广告 今天

WebSockets 与 SSE 与长轮询 — 实时通信无需恐慌

更新于

WebSocket、SSE 和长轮询各有其应用场景。以下是何时使用每种技术——附带比较表格、代码示例和决策指南。

WebSocket与SSE与长轮询——实时通信无需恐慌1
广告 移除?

大多数开发者默认选择WebSocket,但这通常是错误的。

并非因为WebSocket本身不好——它们在自身职责上表现卓越。但它们带来了真实的基础设施开销:负载均衡器上的粘性会话、自定义的心跳逻辑、身份验证的变通方案,以及代理配置在用户在企业办公室打开应用时会失效。对于大多数“实时”需求而言,这种开销是不合理的。

简而言之:如果数据仅从服务器流向客户端,使用服务器发送事件(SSE)。如果客户端需要以低延迟方式推送数据,使用WebSocket。如果刷新频率以秒为单位,并且你希望部署尽可能简单,长轮询仍然有效——这并不丢人。

对比分析

技术方向浏览器支持自动重连负载均衡器复杂性较小——只是一个 ID
长轮询服务器 → 客户端普遍的手动(客户端重新请求)无(标准HTTP)低频更新,≥15秒间隔
SSE服务器 → 客户端所有现代浏览器(不包括IE11)内置(EventSource)无(标准HTTP)实时流、通知、仪表板
WebSocket双向所有现代浏览器手动(自定义心跳)需要粘性会话聊天、游戏、协同编辑

长轮询

长轮询是标准HTTP轮询的一种形式,服务器会保持请求打开,直到有数据可返回,或超时触发。客户端发送请求,服务器等待,当数据可用时返回响应,然后客户端立即发起下一次请求。这本质上是一个循环伪装成网络调用。

这在多种场景下确实适用:

  • 通知徽标 —— 每30秒更新一次的未读数量无需持久连接。
  • 管理仪表板,对数据新鲜度要求宽松 —— 每15到60秒更新一次的指标,使用轮询即可满足。
  • 在连接不可靠的移动应用中 —— 持久连接容易被激进的网络管理机制中断;轮询可以在每次请求中干净地重新连接。
  • 在企业代理后端 —— 许多企业代理会缓冲或终止非标准连接。HTTP轮询在任何地方都有效,没有例外。

可扩展性的限制是真实的。每个保持打开的请求都会占用一个连接槽。在多线程服务器上这会变得昂贵;在事件循环服务器(如Node.js、Tornado、Go的goroutines)上虽然可以管理,但并非免费。当并发用户达到数万级别时,服务器资源的计算开始变得重要。

服务器发送事件(SSE)

SSE是大多数开发者在转向WebSocket时跳过的选项。对于任何数据单向从服务器流向客户端的场景而言,这是一个错误。

它基于标准HTTP运行。服务器设置 Content-Type: text/event-stream 并持续将换行分隔的消息写入响应体。浏览器原生的 EventSource API会自动处理重连——包括一个 Last-Event-ID 头,以便在连接中断后服务器可以恢复流。你可以获得命名事件类型、可配置的重试间隔,且无需任何库。

const source = new EventSource('/api/events');

source.addEventListener('priceUpdate', (e) => {
  const { price, symbol } = JSON.parse(e.data);
  updateTicker(symbol, price);
});

source.onerror = () => {
  // EventSource reconnects automatically — nothing to do here
};

SSE的优势在于:

  • 标准HTTP —— 可通过负载均衡器、反向代理和CDN,无需配置即可工作。无需粘性会话。
  • 自动重连 —— 内置于规范中。在流中设置 retry: 字段以配置间隔;客户端负责其余部分。
  • HTTP/2 多路复用 —— 消除了HTTP/1.1中每个域名最多6个连接的限制。如果你使用的是HTTP/2(你应该使用),那么这一点不再重要。
  • 更简单的服务器实现 —— 保持一个连接打开并持续写入。无需协议握手,无需帧解析,无需心跳逻辑。

唯一的实际限制是:SSE是单向的。客户端无法通过SSE连接发送数据。在实践中,这很少成为问题——对于客户端需要发送的数据,使用常规的POST请求,同时使用SSE流接收服务器事件。两者可以共存而无冲突。

了解HTTP/1.1的连接限制很重要。浏览器限制每个域名在所有标签页中最多6个同时连接。三个浏览器标签页每个消耗两个SSE流将导致限制被触发。HTTP/2通过多路复用消除了这一限制。如果你无法保证HTTP/2的交付(某些较旧的CDN配置,某些企业代理),请记住这一点。

WebSocket

当确实需要双向、低延迟通信时,WebSocket才是正确的工具。能够证明其复杂性值得的场景包括:

  • 聊天 —— 每个用户的每条消息都需要在近实时内传递给其他用户。WebSocket在此类场景中是标准,有充分理由。
  • 多人游戏 —— 游戏状态在客户端之间持续同步,通常每秒更新20到60次。没有其他方法能接近这种每帧的效率。
  • 协同编辑 —— 基于CRDT或OT的实时编辑(如Notion、Figma、Google Docs风格)要求每个按键的输入以最小延迟传播。
  • 交易终端 —— 每秒100毫秒以下的价格流,以及在同一个连接上提交订单。WebSocket正是为此而设计的。

SSE和轮询避免的基础设施成本:

  • 粘性会话 —— WebSocket连接是状态性的,并绑定到单个服务器进程。负载均衡器需要会话亲和性(IP哈希或基于Cookie)来正确路由重连。否则,重连的客户端可能被路由到一个不知道其会话的服务器。
  • 代理和CDN配置 —— Nginx、HAProxy和Cloudflare都支持WebSocket,但需要显式配置(UpgradeConnection 头, proxy_http_version 1.1 在Nginx中)。一些企业防火墙会阻止 101 Switching Protocols。你将在特定办公室用户的支撑票中发现这个问题。
  • 身份验证复杂性 —— WebSocket在初始握手后无法设置自定义头。令牌认证通常意味着将令牌放在查询字符串中(不美观,常见)或在连接后的第一条消息中传递(更干净,但需要服务器端的门控逻辑)。
  • 心跳机制 —— 规范不要求心跳保持。如果没有自定义的ping/pong机制,你将直到下一条消息失败时才检测到连接已死。要么实现心跳机制,要么接受连接逐渐变旧而沉默累积。

这些都不是障碍——它们是可解决的。它们是SSE或HTTP轮询中不存在的复杂性。如果你为了通知流或实时仪表板选择WebSocket,你就是在为没有理由的复杂性买单。

如何选择

按以下顺序逐一分析:

  1. 客户端是否需要以高频向服务器推送数据,或者延迟低于200毫秒是否至关重要? 否 → 跳过WebSocket。
  2. 数据是否仅从服务器流向客户端? 是 → SSE几乎肯定是正确的选择。
  3. 你是否使用HTTP/2? 如果是,SSE在大多数场景下没有实际限制。如果不是,则需考虑6个连接限制。
  4. 你的部署是无服务器的,还是位于不支持持久连接的基础设施之后? SSE在大多数无服务器平台(如Vercel、Cloudflare Workers通过Streams API)上都能工作;WebSocket在无服务器环境需要额外的配置。
  5. 你的刷新频率是否为15秒或以上? 长轮询。保持简单。

如果你已经完成了上述分析,结果仍然是WebSocket——很好。现在你是在为正确的原因使用它们,而不是默认选择。

调试事件负载

SSE data: 字段和WebSocket消息几乎总是携带JSON。当你调试一个行为异常的流时,将原始负载粘贴到 IO Tools’ JSON 格式化器 中是最快查看结构的方式——特别是对于嵌套事件对象,一个缺失的括号或多余的逗号会静默地导致解析失败。

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?