WebSocketとSSEの基本的な違い

リアルタイムでデータを送受信するWebアプリを作るとき、WebSocketとSSE(Server-Sent Events)の2つが主な選択肢になります。どちらもHTTPをベースにしていますが、通信の方向性と接続の仕組みが根本的に異なります。

WebSocket

WebSocketはHTTPのハンドシェイクで始まり、その後は独自のプロトコルに切り替わります。クライアントとサーバーの両方から任意のタイミングでメッセージを送れる「双方向通信」が特徴です。

一度コネクションが確立されると、どちらからでもいつでもメッセージを送れます。リアルタイムチャット・オンラインゲーム・コラボ編集ツールのように、クライアントとサーバーが対等にやり取りする場面で威力を発揮します。

SSE(Server-Sent Events)

SSEは通常のHTTP接続を使い、サーバーからクライアントへの一方向のストリームです。標準のHTTPで動くため、既存のHTTPインフラ(ロードバランサー・プロキシ・CDN)でそのまま使えます。

クライアントからサーバーへのデータ送信には別途通常のHTTPリクエストを使います。「サーバーが継続的にデータを送り続ける」という一方向のユースケースに特化しています。

LLMストリーミングにはどちらが向くか

ChatGPTのような「文字が流れる」UIを実装するとき、多くの場合SSEが適しています。

LLMストリーミングの通信パターン

  1. ユーザーがメッセージを送信(クライアント→サーバー)
  2. サーバーがLLM APIを呼び出し、生成トークンを順次クライアントへ送信(サーバー→クライアント)
  3. 生成完了でストリームを閉じる

このパターンでは「クライアント→サーバー」は1回の通常HTTPリクエスト、「サーバー→クライアント」がストリームです。双方向の常時接続は必要なく、SSEで十分に実現できます。

SSEを選ぶ理由

実装がシンプルです。HTTPの text/event-stream Content-Typeを使うだけで、専用のWebSocketサーバーが不要です。

サーバーレス環境でも動かせます。VercelのEdge FunctionsやCloudflare Workersでもストリーミングレスポンスとして扱えます(後述の制約に注意)。

HTTPの標準機能なのでリトライが自動化されています。SSEは接続が切れると自動で再接続を試みます(EventSource APIを使う場合)。

WebSocketを選ぶべき場面

LLMアプリでも以下のような場合はWebSocketが適しています。

音声のリアルタイム処理

OpenAIのRealtime APIのように、音声入力をリアルタイムにLLMに送りながら、音声出力もリアルタイムにストリーミングで受け取る場合は双方向の低遅延通信が必要です。SSEでは音声データをクライアントからリアルタイムに送り続けることができません。

ツール実行の双方向インタラクション

エージェントが「確認が必要な場面」でユーザーに問い合わせながら処理を進める場合、サーバーとクライアントが頻繁に交互にメッセージを送り合います。このような双方向インタラクションはWebSocketの方が自然に設計できます。

複数ユーザー間の同期

コラボレーション機能(複数人が同時に編集できるドキュメントなど)では、あるユーザーの変更を他のユーザーにリアルタイムで届ける必要があります。WebSocketのコネクションをユーザーごとに管理するサーバーサイドの設計が必要です。

実装コストの比較

SSEの実装コスト

バックエンドはHTTPのContent-TypeをSSE用に設定してレスポンスを流し続けるだけです。前述のNext.js・FastAPIの例のように数十行で実装できます。フロントエンドはEventSource APIか fetch + ReadableStreamを使います。

追加のインフラ(WebSocketサーバー・ライブラリ)は不要で、既存のHTTPサーバーをそのまま使えます。

WebSocketの実装コスト

WebSocketサーバーの管理が必要です。Node.jsならwsライブラリ、PythonならFastAPIのWebSocketサポートなどを使います。

本番運用ではロードバランサーをWebSocket対応に設定する必要があります(スティッキーセッションの設定など)。スケールアウトする場合は、コネクションの管理にRedisなどを使った共有メモリが必要になります。

サーバーレス環境での制約

SSEとサーバーレス

Vercel、AWS Lambda、Cloudflare Workersなどのサーバーレス環境はリクエスト1回で1レスポンスを返す設計が基本です。SSEのストリーミングはサポートされていますが、実行時間の上限(Vercelなら60秒、Lambda デフォルト15秒)に注意が必要です。

Vercel Edge FunctionsとCloudflare WorkersはWeb標準のReadableStreamをサポートしており、比較的制約が少ないです。

WebSocketとサーバーレス

WebSocketは接続が継続するため、リクエスト単位で課金されるサーバーレスとの相性が悪いです。WebSocketを使いたい場合は専用サーバー(EC2・GCP Compute Engine・Fly.io など)か、WebSocket専用のマネージドサービス(Ably・Pusher・AWS API Gateway WebSocket)が現実的です。

選択の実践的な判断基準

以下の問いに「はい」が多いほどSSEが適しています。

  • ユーザーが質問を送ってサーバーが答えを流す、という一方向の流れが中心か
  • サーバーレス環境でシンプルに始めたいか
  • 追加インフラを最小限にしたいか

以下の問いに「はい」が多いほどWebSocketが適しています。

  • クライアントとサーバーが頻繁に交互にメッセージを送り合うか
  • 音声やバイナリデータをリアルタイムに送る必要があるか
  • 複数ユーザー間のリアルタイム同期が必要か

まとめ

LLMのテキストストリーミングには多くの場合SSEで十分で、実装コストも低く抑えられます。音声のリアルタイム処理・双方向インタラクション・マルチユーザー同期が必要な場合にWebSocketを選ぶのが合理的な判断です。サーバーレスで始める場合はSSEが自然な選択で、スケールとともにWebSocketへの移行を検討するという順番が現実的です。