WebSocketsSSE
1)ショート: 何のために
WebSocket (WS/WSS)-HTTP接続をフルデュプレックスチャンネルにアップグレードします。チャット、ライブゲーム、コラボレーション、双方向テレメトリーに適しています。
Server-Sent Events (SSE)-サーバからブラウザへの一方通行ストリーム(MIME 'text/event-stream')。ティッカー、通知、引用符、タスクの進行に最適です。クライアント-'EventSource'。
- リアルタイム(多くの場合)→WebSocketでクライアントからの入力が必要です。
- サーバーからのプッシュ更新のみ、互換性とシンプルさはSSE→よりも重要です。
2)ネットワークおよび議定書
2.1輸送と互換性
WebSocket: HTTP 'GETとして起動します……アップグレード:websocket '(HTTP/1。1).HTTP/2では、RFC 8441 (CONNECT+':protocol=websocket')が可能で、サポートはプロキシによって異なります。TLS (WSS)の上で働く-販売で必須。
SSE:ストリーミングを使用した通常の長いHTTPレスポンス('200 OK')。それはHTTP/1を通ってよく行きます。1/2/3、 CDN/プロキシと互換性があります(長い接続が終了していない場合)。
2.2 プロキシ/バランサー/CDN
チェック:長時間の接続、アイドルとタイムアウト、スティッキーセッション(ステートがノード上にある場合)のサポート。
WSの場合:'proxy_read_timeout'、 'upgrade'ヘッダ、接続ごとの制限を含める。
SSEの場合、プロキシがレスポンスをバッファしないことを確認します(そうでなければ、クライアントは時間内にイベントを表示しません)。
3)メッセージモデルとフロー制御
WebSocket:テキストまたはバイナリフレーム;'ping/pong'がありますが、組み込みのバックプレッシャーはありません-アプリケーション(キュー、ウィンドウ、ドロップポリシー)に実装します。
SSE:テキストイベント(UTF-8);クライアントは、構築された遅延と再接続することができます。サーバは'retry:'を指定できます。希望する位置から再開するには'id:'と'Last-Event-ID'ヘッダーがあります。
- クライアントごとの送信メッセージを引用します。
- unsentイベントのキューを制限します。overflow-低優先度/集計を破棄します。
- WSでは、スライディングウィンドウとアプリケーションレベルのACKを使用します。
4)接続の信頼性
4.1検出およびkeepalive
WS: N秒ごとに'ping'を送信します。タイムアウトギャップ-指数関数バックオフ+ジッタで再接続します。
SSE:サーバーは「comments」 ':\n'をハートビートとして送信し、接続がアイドル状態にならないようにします。クライアントは自分自身を再接続します。
4.2フローリカバリ
WS:オフセット/シーケンスメッセージを保持し、再接続後にデルタを要求します。
SSE:各イベントには'id:'を使用し、リクエストには'Last-Event-ID'を使用します。サーバーは欠落したイベントを送信します。
5)認証と認証
要求URL (WS)のJWT Bearerは安全ではありません(ログの漏洩)。(プライマリHTPハンドシェイクを介して)ヘッダーまたはフラグ'Secure'、 'HttpOnly'、 'SameSite'のクッキーを使用します。
mTLS(特にB2Bの場合)、および元の要求に対する署名(HMAC)が可能です。
Cookieを使用するSSEについては、CORS ('Access-Control-Allow-Origin'、 'Allow-Credentials')について覚えておいてください。
トークン回転:ストリームを切り取らないでください。パス「will expire soon」→クライアントは新しいトークンとの接続を再開します。
6)データ形式および圧縮
WebSocket: permessage-deflateを慎重に有効にする(CPU);既に圧縮されているフォーマット(Proto、 Avro)の圧縮は避けてください。バイナリペイロードはJSONよりも経済的です。
SSE:これはテキストです。大きなデータの場合は、REST/gRPCリソースまたはチャンクファイルへのリンクを送信します。SSE用のトランスポートgzip-適切ですが、proxy/CDNでバッファリングするのに注意してください。
7)スケーリングとファンアウト
7.1水平スケーリング
再起動のためにアプリの不具合がないようにしてください。接続ステータス-フロントレイヤー;データ-ブローカーから。
ローカルキューがある場合は、Sticky(セッション/ユーザーによるハッシュ)が必要です。
理想はステートレスフロントです。ノードはサブスクリプションを多重化するだけです。イベントは共通のパブ/サブから来ます。
7.2パブ/サブブローカー
広いファンアウトの場合は、Kafka/NATS/Redis Streamsを使用します。
「Fanout Gateway」レイヤーは、WS/SSEを介してトピックとフラフクライアントを購読します。
ルーティングキー('userId'、 'matchId'など)を使用して、ノード間の負荷をバランスさせます。
8)生産の構成
8.1 NGINX-WebSocket
nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server {
listen 443 ssl http2;
server_name ws. example. com;
location /ws {
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1. 1;
proxy_read_timeout 75s; # increase for long sessions proxy_send_timeout 15s;
proxy_pass http://ws-backend;
}
}
8.2 NGINX-SSE(バッファリングを無効にするために重要)
nginx location /events {
proxy_http_version 1. 1;
proxy_set_header Connection "";
proxy_buffering off; # is critical for proxy_cache off threads;
chunked_transfer_encoding on;
proxy_read_timeout 60m;
proxy_pass http://sse-backend;
}
8.3 Kubernetes (Ingress Annotations、 NGINX Ingress)
yaml metadata:
annotations:
nginx. ingress. kubernetes. io/proxy-read-timeout: "3600"
nginx. ingress. kubernetes. io/proxy-send-timeout: "3600"
nginx. ingress. kubernetes. io/enable-websocket: "true"
nginx. ingress. kubernetes. io/proxy-buffering: "off" # для SSE
9)コード例
9.1 SSEクライアント(ブラウザ)
js const es = new EventSource("/events? channel=odds", { withCredentials: true });
es. addEventListener("message", (e) => {
const data = JSON. parse(e. data);
renderOdds(data);
});
es. addEventListener("error", () => {
//EventSource will reconnect itself; can be shown spinner
});
9.2 SSEサーバ(ノード。js/Express)
js app. get('/events', (req, res) => {
res. writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
res. write ('retry: 3000\n\n') ;//3s backoff to client
const sub = subscribe(req. query. channel, (event) => {
res. write(`id: ${event. id}\n`);
res. write(`event: message\n`);
res. write(`data: ${JSON. stringify(event. payload)}\n\n`);
});
req. on('close', () => sub. unsubscribe());
});
9.3 WebSocketクライアント(ブラウザ)
js const ws = new WebSocket("wss://ws. example. com/ws");
ws. onopen = () => ws. send(JSON. stringify({ type: "join", room: "chat-1" }));
ws. onmessage = (m) => handle(JSON. parse(m. data));
ws. onclose = () => scheduleReconnect();
10)セキュリティと制限
接続ごとおよびユーザーごとのレート制限:受信メッセージのレート(WS)と送信フローのレート(WS/SSE)を制限します。
接続寿命と総トラフィックによるクォータ。
メッセージサイズ制限の最大メッセージ/秒。
ハンドシェイクステージのWAF/ボットフィルタ。接続の洪水(多くの短い関係)に対する保護。
テナント/名前空間の分離:個々のリソースプール。
11)観測可能性
メトリクス:- 'connections_active'、 'connections_new_total'、 'bytes_in/out'、
- 'messages_in/out_total'、 'dropped_messages_total'、
- 'reconnects_total'、 'latency_delivery_ms {p50、 p95、 p99}'。
- ログ:IP/UA、 userId/tenantId、閉じる理由('close_code')、期間。
- トレース:イベントを元のコマンド(相関ID)に関連付けます。WSの場合は「virtual」 butchスパンを使用します。
12)運用上のニュアンス
クライアント(CDN/edge)に近いTLS終了。
depleys中の接続のプロアクティブな回転(優雅な):フラグに「再接続」を与えます。
「熱い」チャネルを均等に分配するキーのsharding。
Late Subscriberのステータススナップショット(スナップショット+デルタ)。
最後の値キャッシュ(特にSSE)-コールドクライアントに便利です。
13)アンチパターン
SSEまたはJSON経由でWS経由で大きなバイナリチャンクをストリーミングする-メッセージにHTTPダウンロードとリンクを使用します。
接続時のみ承認し、長いセッションの再検査がない場合。
不必要にグローバル粘着性のある→不均衡とホットノード。
無効なタイムアウト/制限→フリーズした接続はプールを使い果たします。
SSE プロキシ/CDNバッファリング→「リアルタイム」が遅延分になります。
シーケンス/オフセットの欠如→再接続後、クライアントはデータの整合性を失います。
14)実装チェックリスト
- WS(双方向)またはSSE(片側)が選択されます。
- 設定されたタイムアウト、キープアライブ、再接続(バックオフ+ジッタ)。
- シーケンス/オフセットと(SSE用)'id'/'Last-Event-ID'を設計しました。
- サイズ/速度/接続/クォータ、DoS保護定義。
- Proxy/ingress config: upgrade、 proxy_buffering off (SSE)、 read/sendタイムアウト。
- スケーリング:パブ/サブブローカー、ファンアウトゲートウェイ、必要に応じてのみ粘着性があります。
- 認証:セキュアトークン転送、ノーブレイク回転。
- Observability:メトリック、ログ、トレース;ダッシュボードとアラート。
- リリースプラン:graceful-drain接続、クライアントに再接続を知らせます。
- ゲームデイズ:ネットワークブレイク、ノードドロップ、ブローカーの過負荷、長いRTT。
15) FAQ
CDN経由でSSEをキャッシュできますか?
通常ではありません:それはパーソナライズされた流れです。パブリックチャンネルの場合-おそらく短いTTLとチャンクデリバリーがありますが、「リアルタイム」を破るのは簡単です。
WebSocketはHTTP/2/3の上で動作しますか?
ブラウザWSはHTTP/1から始まります。1-upgrade;h2にはRFC 8441があり、プロキシ/サーバーでのサポートが別途必要です。h3を使って-動きで;ストリーミングのために、h3はWebTransportを持っていますが、それは別のAPIです。
gRPC vs WS for Browser?
クリーンブラウザはgRPCとは言いません。Envoy経由でgRPC-Webが必要です。インタラクティブなUIの場合、WS+RESTの方が簡単です。
16)合計
WebSocket-リアルタイムの対話とコンパクトな双方向チャネルが必要な場合。
SSE-サーバーからクライアントへの簡単で信頼性の高いプッシュが必要な場合は、複雑さを最小限に抑え、自動的に再接続します。
売上の成功は、正しいタイムアウトと限界、オフセットからの回復、パブ/サブファンアウト、正しいプロキシ設定、明確なオブザビリティです。