不喜欢广告? 无广告 今天

WebSockets 与 SSE 与长轮询 — 在你的聊天应用崩溃前选择正确的实时模式

更新于

大多数开发者默认使用WebSocket而未深思。本文详细介绍了WebSocket、服务器推送事件和长轮询的真正权衡——延迟、重连行为、基础设施复杂性、双向通信——帮助您选择合适的模式,从而避免过度设计数据流。

WebSockets vs SSE vs Long Polling — Pick the Right Real-Time Pattern Before Your Chat App Melts 1
广告 移除?

You built a dashboard. Users need live data updates. You reached for WebSockets — because that’s what everyone does. Now you’re debugging sticky sessions at 2 AM because your load balancer keeps sending clients to the wrong pod. The data you’re pushing? A single JSON number every 10 seconds. WebSockets were overkill.

This happens constantly. Developers treat WebSockets as the default real-time solution, when SSE or long polling would have shipped faster, scaled more easily, and cost less to operate. Here’s how to actually choose.

The Decision Table

Before the deep dive, here’s the full comparison. Use this to make a call, then read the sections below if you need the reasoning.

特征长轮询Server-Sent EventsWebSocket
协议HTTP/1.1HTTP/1.1+ (chunked)WS (HTTP upgrade)
方向Client pulls onlyServer → Client onlyFull bidirectional
浏览器支持All browsersAll modern (no IE11)所有现代浏览器
自动重连Manual — you write the loopBuilt-in via EventSourceManual or via library
CDN / proxy friendly是的Mostly (watch timeouts)
Horizontal scalingStateless, trivialStateless, trivialRequires sticky sessions or pub/sub broker
Infra complexityLow — plain HTTPLow — plain HTTPHigh — stateful connections, LB config, broker
LatencyPoll interval + round tripNear real-timeNear real-time
较小——只是一个 IDLow-frequency updates, legacy infraDashboards, feeds, notificationsChat, collaborative editing, gaming

Long Polling: The Unsexy Workhorse

Long polling is not “old” or “wrong” — it’s just HTTP. The client makes a request, the server holds it open until there’s something to say (or a timeout fires), the client gets the response and immediately fires the next request. Repeat.

It’s been around since before WebSockets existed and it still runs millions of production apps. The reason: it’s just HTTP. Your existing reverse proxy, CDN, load balancer, and monitoring all understand it without configuration. No sticky sessions, no protocol upgrades, no WS-aware infrastructure.

The downsides are real though. Every “update” is a full HTTP request cycle — headers, TCP overhead (unless HTTP/2), server-side request queuing. Under high concurrency this gets expensive on the server. And the latency floor is the round-trip time of each poll, not the latency of the event itself. If you’re polling every 5 seconds and an event fires at second 1, the user waits 4 more seconds.

When to use it: Legacy systems where adding a persistent connection layer isn’t worth the infra change. Low-frequency updates (once per minute or less). Environments where SSE gets killed by overzealous proxies. Polling that clients explicitly control — job status checks, async task results.

Server-Sent Events: The One Everyone Forgets

SSE is a standard (HTML Living Standard) that lets a server push a stream of text events over a single long-lived HTTP connection. The browser handles reconnection automatically. You don’t write a reconnect loop — the EventSource API just reconnects with the last event ID it saw, so you can resume a stream after a network blip.

The wire format is embarrassingly simple:

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

id: 1
event: price-update
data: {"symbol":"BTC","price":67420}

id: 2
event: price-update
data: {"symbol":"ETH","price":3521}

Each event is plain text with optional id, event, data,并且 retry fields, separated by blank lines. You can test SSE endpoints directly with cURL — use the cURL 命令生成器 to construct the request with the right Accept header (text/event-stream) and stream the response.

On the client side:

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

es.addEventListener('price-update', (e) => {
  const payload = JSON.parse(e.data);
  updateDashboard(payload);
});

es.onerror = () => {
  // EventSource will auto-reconnect. You don't need to do anything here
  // unless you want to log or update UI state.
};

SSE has one real limitation: it’s server-to-client only. The client can’t push data back over the same connection — any client-initiated actions go over normal HTTP requests. For most dashboard, notification, and feed use cases, that’s fine. You weren’t using the client-to-server direction anyway.

The other gotcha: some proxies buffer the response body and only forward it after the connection closes, which breaks streaming entirely. NGINX needs proxy_buffering off. Cloudflare’s free tier buffers SSE. If your infra has a buffering proxy in the middle and you can’t configure it, SSE won’t work and you’ll need WebSockets or long polling.

When to use it: Live dashboards, notification feeds, activity logs, AI response streaming (this is exactly what ChatGPT’s streaming API uses), stock tickers, deploy/build log tailing. Anywhere the server has data to push and the client doesn’t need to push back.

WebSockets: When They’re Actually Worth It

WebSockets give you a full-duplex, persistent connection over a single TCP socket. After the HTTP upgrade handshake, both sides can send framed messages at any time. The latency is as low as you can get over a network — no new request per message, no headers on each frame.

The tradeoff is that they’re stateful. The server needs to track every open connection. This immediately creates problems at scale: load balancers route by connection (not request), so you need sticky sessions or a pub/sub broker (Redis, etc.) to fan out messages across multiple server instances. Your CDN can’t cache WebSocket traffic. Your reverse proxy needs explicit configuration for the upgrade. Your monitoring needs to understand WS frames if you want payload-level visibility.

None of this is insurmountable, but it’s real work. When you’re starting a project and evaluating options, “it’s just HTTP” vs. “stateful WS connections with a Redis pub/sub layer” is a meaningful difference in complexity.

WebSocket payloads are typically JSON. If you’re debugging a WS integration and need to inspect or format the messages, the JSON 格式化程序 makes it easier to read minified payloads from your dev tools network tab.

何时使用: Chat applications (genuinely bidirectional — every message is client-initiated). Collaborative editing (Figma, Google Docs — multiple clients writing to shared state). Multiplayer games (high-frequency, low-latency state sync). Financial trading terminals (sub-100ms latency on order books). Basically: anything where the client needs to push data frequently, not just receive it.

The Scaling Gotcha Nobody Mentions Until It’s Too Late

Long polling and SSE are stateless at the HTTP layer. Each request can land on any server instance. Your horizontal scaling story is boring and that’s a feature: add more instances, done. Connection state lives only for the duration of the open HTTP connection, and load balancers route freely.

WebSockets are stateful. Client A’s connection lives on Server 1. When you want to push a message to Client A from Server 2 (because the event originated there), Server 2 has to know about that connection. The standard solution is a pub/sub broker — Redis pub/sub, Kafka, or managed services like Pusher or Ably that abstract all of this away. You either build that layer yourself or you pay for it.

Pusher charges $49/month for 500 concurrent connections. Ably is similarly priced. If your “real-time feature” is a notification badge that updates when someone likes your post, that’s a lot of money for SSE you could host for free alongside your existing API.

HTTP/2 Changes the Long Polling Math

Classic HTTP/1.1 long polling is one request per connection per in-flight hold. Under HTTP/2, multiplexing means multiple streams over one TCP connection. The overhead is lower. This isn’t a reason to pick long polling over SSE, but it means the “HTTP overhead is expensive” criticism applies less in 2024 than it did in 2012 when these comparisons were first written.

If you’re on HTTP/2 end-to-end (client to server, no intervening proxy that downgrades), long polling scales better than most older benchmarks suggest.

Quick Decision Guide

Stop reading here if your use case fits one of these cleanly:

  • Update frequency < 1/minute, legacy infra, simple polling semantics: Long polling. Don’t complicate it.
  • Server pushes data, client mostly reads, standard HTTP infra: SSE. This covers 80% of “real-time” features.
  • Client sends data frequently, latency < 100ms matters, truly bidirectional: WebSockets. Now it’s worth the complexity.
  • You’re using AI streaming (OpenAI, Anthropic, etc.): SSE — that’s literally what their streaming APIs use.
  • Multiplayer game or collaborative editor: WebSockets, or a purpose-built framework (Liveblocks, PartyKit, etc.).

The default answer isn’t WebSockets. It’s SSE — until you have a specific reason it isn’t enough. The number of production apps that actually need full bidirectional real-time is much smaller than the number that 使用 WebSockets. That gap is engineering debt accumulated at 2 AM in front of a load balancer config.

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

安装我们的扩展

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

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

记分板已到达!

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

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

新闻角 包含技术亮点

参与其中

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

给我买杯咖啡
广告 移除?