WebSockets vs SSE vs Long Polling — リアルタイムを安心して実現する
WebSockets、SSE、長時間ポーリングはそれぞれに適した場面があります。それぞれに使うべきタイミングを紹介し、比較表、コード例、そして決定ガイドを提供します。
多くの開発者はデフォルトでWebSocketsを選びがちですが、それは通常間違っています。
WebSocketsが悪いわけではありません — それらは自分がやるべきことにおいて優れています。しかし、実際のインフラ負荷を伴います:ロードバランサでのスタイックセッション、カスタムなハートビートロジック、認証の補助手段、そしてプロキシ設定が、誰かがあなたのアプリを企業のオフィスで開くまで問題なく機能する場合があります。ほとんどの「リアルタイム」要件において、その負荷は正当化されていません。
短い版:データがサーバーからクライアントへのみ流れている場合は、サーバーからクライアントへ送信するサーバー送信イベント(SSE)を使用します。クライアントが低遅延でデータを送信する必要がある場合は、WebSocketsを使用します。リフレッシュ周期が秒単位で、最もシンプルなデプロイメントを望む場合は、長距離ポーリングも機能し、そのような選択に恥じないのです。
CUID2
| 技術 | 方向 | ブラウザサポート | 自動再接続 | ロードバランサの複雑さ | 最適な用途 |
|---|---|---|---|---|---|
| 長距離ポーリング | サーバー → クライアント | ユニバーサル | 手動(クライアントによる再要求) | なし(標準HTTP) | 低頻度更新、15秒以上の間隔 |
| SSE | サーバー → クライアント | すべての現代ブラウザ(IE11を除く) | 組み込み(EventSource) | なし(標準HTTP) | ライブフィード、通知、ダッシュボード |
| WebSockets | 双方向 | すべての現代ブラウザ | 手動(カスタムハートビート) | スタイックセッションが必要 | チャット、ゲーム、協働編集 |
長距離ポーリング
長距離ポーリングは、標準HTTPポーリングで、サーバーがデータを返すまでまたはタイムアウトが発生するまでリクエストを保持するものです。クライアントがリクエストを送信し、サーバーが待機し、データが可用になったら応答を返し、クライアントがすぐに次のリクエストを送信します。これはネットワーク呼び出しの装飾されたループです。
これは、さまざまな用途に本当に適しています:
- 通知バッジ — 30秒ごとに更新される未読数は、持続的な接続を必要としません。
- 管理ダッシュボードで鮮度が緩い場合 — 15~60秒ごとに更新されるメトリクスは、ポーリングで十分に機能します。
- 不安定な接続を持つモバイルアプリ — 持続的な接続は、積極的なネットワーク管理によって切断され、ポーリングは各リクエストでクリーンに再接続されます。
- 企業プロキシの後ろ — 許可されていない非標準接続をバッファリングまたは終了する多くの企業プロキシがあります。HTTPポーリングはどこでも機能し、例外はありません。
スケーリングの注意点は現実です。保持されたリクエストは接続スロットを消費します。スレッドベースのサーバーではこれは高価になりますが、イベントループベースのサーバー(Node.js、Tornado、Goのgoroutines)では管理可能ですが、無料ではありません。1万ユーザー以上の場合、サーバー資源の計算が重要になります。
サーバー送信イベント(SSE)
SSEは、開発者がWebSocketsへ向かってスキップする最も一般的な選択肢です。データがサーバーからクライアントへ一方向に流れているすべての用途において、これは誤りです。
それは標準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の1ドメインあたり6接続の制限を削除します。HTTP/2(あなたはそれをすべきです)なら、これは問題ではありません。
- サーバー実装の簡易性 — 接続を保持し、それを書き込みます。プロトコルハンドシェイク、フレーム解析、ハートビートロジックは不要です。
唯一の制限は、SSEは一方向性です。クライアントはSSE接続を通じてデータを送信できません。実際にはこれはほとんど問題ではありません — クライアントが送信する必要があるすべてのデータは、通常のPOSTリクエストを使用し、SSEストリームはサーバーイベントに使用します。これらは問題なく共存します。
HTTP/1.1の接続制限を知ることが重要です。ブラウザはすべてのタブで1ドメインあたり6つの同時接続を制限しています。3つのブラウザタブそれぞれが2つのSSEストリームを消費すると制限に達します。HTTP/2はマルチプレクシングによりこれを解除します。HTTP/2の配信を保証できない場合(一部の古いCDN設定、一部の企業プロキシ)には、これを意識してください。
WebSockets
WebSocketsは、本当に双方向、低遅延メッセージングが必要な場合に適したツールです。その使用ケースは次の通りです:
- チャット — すべてのユーザーからのメッセージが、ほぼリアルタイムで他のユーザーに届く必要があります。WebSocketsはその理由で標準です。
- マルチプレイヤーゲーム — ゲーム状態は常にクライアント間で同期され、20~60回の更新/秒で行われます。他のアプローチはそのフレームあたりの効率に近づけません。
- 協働編集 — CRDTまたはOTベースのリアルタイム編集(Notion、Figma、Google Docs風)では、各キーボード操作が最小の遅延で伝播する必要があります。
- トレーディングターミナル — 100ms未満の価格フィードと、同じ接続上で注文の提出。WebSocketsはこれに設計されています。
SSEおよびポーリングが回避するインフラコスト:
- スタイックセッション — WebSocket接続は状態を持ち、1つのサーバープロセスに結びついています。ロードバランサはセッション親和性(IPハッシュまたはクッキーベース)を必要として、再接続を正しくルートします。それがないと、再接続するクライアントはそのセッションを知らないサーバーに移動する可能性があります。
- プロキシおよびCDN設定 — Nginx、HAProxy、CloudflareはすべてWebSocketsをサポートしていますが、明示的な設定(
UpgradeとConnectionヘッダー、proxy_http_version 1.1Nginxで)が必要です。一部の企業ファイアウォールは101 Switching Protocolsをブロックします。特定のオフィスのユーザーからのサポートチケットでその問題に気づくでしょう。 - 認証の複雑さ — WebSocketsはハンドシェイク後のカスタムヘッダーを設定できません。トークン認証は通常、クエリ文字列(醜い、一般的)または接続後の最初のメッセージにトークンを渡す(きれいだが、サーバー側のゲートロジックが必要)という方法になります。
- ハートビート — スペックはキープアライブを必要としません。カスタムピン/ポンロジックがないと、次のメッセージが失敗するまで死んでいる接続を検知できません。ハートビートを実装するか、静かに古びた接続を積み重ねることを許容します。
これらすべてはブロッカーではありません — それらは解決可能です。それらはSSEまたはHTTPポーリングで存在しない複雑さです。通知フィードやライブダッシュボードのためにWebSocketsを選択している場合、そのコストはまったく必要ないのです。
どのように選ぶか
順番にこれらを確認してください:
- クライアントがサーバーにデータを高頻度で送信する必要があるのか、200ms未満の遅延が重要なのか? いいえ → WebSocketsをスキップします。
- データがサーバーからクライアントへのみ流れているか? はい → SSEはほぼ間違いなく正しい選択です。
- HTTP/2を使用していますか? はいなら、SSEはほとんどの用途において意味のある制限はありません。いいえなら、6接続制限を考慮してください。
- あなたのデプロイがサーバレスか、持続接続をサポートしないインフラの後ろにあるか? SSEはVercel、Cloudflare Workers(ストリームAPI)のほとんどのサーバレスプラットフォームで動作します。WebSocketsはサーバレスでは追加のプラグインが必要です。
- リフレッシュ周期が15秒以上ですか? 長距離ポーリング。シンプルに保ちましょう。
これらを確認した後で、答えがまだWebSocketsなら — いいえ。今、あなたは正しい理由でWebSocketsを使用しています。
イベントペイロードのデバッグ
SSE data: フィールドおよびWebSocketメッセージはほとんどJSONを含んでいます。ストリームが誤動作している場合、raw payloadを IO Tools’ JSON Formatter に貼り付けるのが、構造を一目で確認する最も速い方法です — 特にネストされたイベントオブジェクトで、欠けた括弧や追加のコンマがパースを静かに殺す場合です。
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
