11 ポイント 投稿者 GN⁺ 2025-04-13 | 3件のコメント | WhatsAppで共有
  • WebSocket はリアルタイム通信に有用だが、常に必要とは限らず、HTTP ベースの代替手段のほうがよりシンプルで安定している場合がある
  • トランザクション処理、接続管理、サーバーの複雑性といった面で、WebSocket は過剰なオーバーヘッドを引き起こすことがある
  • HTTP Streaming とライブラリ eventkit を活用すれば、WebSocket なしでもリアルタイム同期やイベント処理が可能

WebSocket とは何か

  • WebSocket はクライアントとサーバーの間に持続的な双方向通信チャネルを開く技術
  • HTTP を通じて接続を開始するが、その後は別のプロトコルで通信が行われる
  • リアルタイムアプリケーションの実装でよく使われ、双方向通信が可能という点で有用

WebSocket メッセージはトランザクション方式ではない

  • WebSocket はリクエストとレスポンスの直接的な対応関係を保証しない
  • 状態変更コマンドとその結果メッセージが同じストリーム上で混在して到着することがある
  • たとえば、1 つのクライアントが状態を変更してエラーが発生しても、どのコマンドに対するエラーなのか把握しにくい
  • 解決策として requestId を含めてコマンドとレスポンスを結び付ける方法があるが、これは複雑性と管理コストを増やす
  • HTTP を使ったトランザクション方式でコマンドを送り、WebSocket は状態変更のブロードキャストのみに使うほうがシンプル
  • 送信側は HTTP リクエスト、受信側は WebSocket または別のストリーミング方式として分離できる

WebSocket 接続ライフサイクル管理の難しさ

  • WebSocket を使う場合、接続の開始、終了、エラー、再接続などを自前で処理しなければならない
  • ブラウザでの基本的な処理例には、接続オープン、メッセージ受信、エラー発生、接続終了のイベントハンドリングが含まれる
  • 再接続ロジック、メッセージバッファリング、指数バックオフなどの追加ロジックが必要になる
  • 一方 HTTP はリクエスト単位で開始と終了が明確なため、実装がシンプル
  • 複雑なライフサイクル管理は、WebSocket を使う理由が明確な場合にのみ正当化される

サーバーコードの複雑性が増す

  • WebSocket では HTTP アップグレードリクエストを処理する必要があり、そのため追加のハンドシェイクロジックが求められる
  • Sec-WebSocket-Key のような特殊ヘッダーを検証し、レスポンスヘッダーを適切に返さなければならない
  • WebSocket 接続後は継続的なメッセージ受信・送信状態を維持する必要があり、部分フレーム処理のような問題も起こりうる
  • HTTP のみを使う場合と比べて、デバッグやエラー処理の難易度が高くなる
  • フレームワークが一部の工程を抽象化してくれても、根本的な複雑性は依然として残る

代替案: HTTP Streaming

  • HTTP はもともとストリーミングをサポートするプロトコルで、ファイル全体ではなくデータストリームをリアルタイムで転送できる
  • 従来 WebSocket が担っていた受信側の機能だけを HTTP ストリーミングで置き換えられる
  • 非同期ジェネレーターを使って状態更新をストリーム形式で処理できる
  • サーバー側の流れ
    • 状態更新はコマンド処理関数で実行される
    • 接続されたクライアントはジェネレーターを通じて、新しい値が出るたびに受け取る
    • 状態変更コマンドは HTTP POST で送信し、リアルタイムストリームは GET リクエストで購読する
  • クライアント側の流れ
    • Fetch API と Stream Reader を通じてリアルタイムデータを受信する
    • テキストをデコードした後に UI を更新する
  • この構成により、WebSocket なしでもリアルタイムな状態同期を実装できる

ボーナス: eventkit ライブラリの紹介

  • eventkit は非同期ストリームを簡単に構成・監視できるようにするライブラリ
  • RxJS に似ているが、副作用管理が改善されており、ジェネレーター ベースで設計されている
  • 状態更新をストリームに push すると、クライアント側でそれをリアルタイムに受信できる
  • Stream および AsyncObservable により、サーバー/クライアントの両方で簡単に実装できる
  • サーバー側での eventkit 活用
    • 状態変更を Stream に push し、クライアントはそのストリームを購読する
  • クライアント側での eventkit 活用
    • ストリームデータを受け取り、デコード後に UI を更新する
  • 公式 GitHub リポジトリと HTTP Streaming ガイドも提供されている

GitHub: https://github.com/hntrl/eventkit

3件のコメント

 
[このコメントは非表示になっています。]
 
[このコメントは非表示になっています。]
 
GN⁺ 2025-04-13
Hacker Newsの意見
  • HTTPストリーミングはこのパターンを念頭に設計されたものではないと思う。HTTPストリーミングは大きなデータを断片に分ける用途だ。ストリーミングを pub/sub メカニズムのように使うと後悔するかもしれない。HTTPの中継者たちはこのトラフィックパターンを想定していない(NGINX、CloudFlare など)。WiFi 接続が切れるたびに fetch API がリクエスト失敗のエラーを投げそうだ

    • WebSockets が不要なケースは多い。Server-Sent Events(SSE)のほうがよりシンプルな解決策だ。SSE が注目されてこなかったのは残念だ
  • RequestID をサーバーに送ってリクエスト/レスポンスのサイクルを得るのは、奇妙でも過剰でもない。本気のアプリなら send(message).then(res => ...) のような API を備えている価値は常にある

    • アップグレードリクエストはややこしい。WebSocket サーバーが HTTP サーバーの中に埋め込まれて統合されないのは煩わしい
    • WebSocket リクエストで headers['authorization'] を読むミドルウェアを再利用する代わりに、リクエストヘッダーのふりをした connectionParams オブジェクトにアクセスしなければならない
    • WebSocket のブラウザ API は EventSource より扱いやすい
  • 動画ストリーミングは、クライアントが範囲指定でチャンクを要求するもので、単一の HTTP 接続ではない

  • EventKit の代わりに SSE を使うのがよい

  • POC では従来の HTTP フォーム送信を使う予定だ。それ以外は必要ない

    • アーキテクトは WebSockets が必要だと主張する
    • POC に XHR や WebSockets は不要だ。順次進む購入フローだからだ
    • 結局、不要な WebSockets を提供することになる
  • HTTP/2 の問題は、サーバープッシュが既存プロトコルの上に追加されたことだ。HTTP はリソース転送プロトコルであり、不要なオーバーヘッドを加える。HTTP/2 の主な目的は、サーバーがファイル/リソースをクライアントに事前プッシュして往復遅延を減らすことだ

    • WebSockets は双方向通信のために設計された、よりシンプルなプロトコルだ。単一接続でデータフローを制御しやすい。状態管理や接続喪失からの回復が容易だ。認証とアクセス制御も単純になる
  • WebSockets はストリームとして送るのではなく、データグラム(パケット)として送るものだ。JavaScript ライブラリの WebSockets API はバックプレッシャーを処理できず、すべてのエラーも処理できない。TCP ストリームとして使うなら注意が必要だ

  • WebSockets を本番投入したあとで後悔した。NGINX が 4/8 時間後に接続を切ったり、ブラウザがスリープ後に再接続しなかったりする問題があった。可能なら WebSockets と長期接続は避けるべきだ

  • WebSockets には理想化された認識がある。ストリーミング/リアルタイム用途に WebSockets を使おうとしがちだ。WebSockets は HTTP ツールのシンプルさと利点を失わせる。ストリーミングなサーバー変更への解決策は h2/h3 と SSE だ。クライアントごとに最大 0.5req/s でバッチ化できるなら WebSockets は必要ない

  • HTTP ストリーミングに関心がある人は Braid-HTTP を確認するとよい。HTTP にイベントストリーミングをエレガントに拡張し、強力な状態同期プロトコルを提供する