0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#0143(2025/05/19)エンジニアのための WebSocket 実践ガイド

Posted at

エンジニアのための WebSocket 実践ガイド

1. WebSocket 概要

WebSocket は RFC 6455 で策定されたフルデュープレックス通信プロトコルです。最初に HTTP/1.1 の Upgrade: websocket 握手を経て、以降は独立した軽量フレームでやりとりするため、毎リクエストごとにヘッダを積む REST‐HTTP とくらべてレイテンシ・帯域の両面で効率的です。HTTP/2 環境でも RFC 8441 (Extended CONNECT) により 1 本の TCP で多重化でき、HTTP/3 では派生技術の WebTransport が UDP-ライクな通信を補完します。

WebSocket フレームは最小 2 byte のヘッダに OPCODE, FIN, MASK などを持ち、クライアント→サーバは必ずマスクが義務づけられる設計です。 Ping/PongClose など制御フレームが標準化されているため、TCP Keep-Alive では測れないアプリ層レベルの生存確認や Graceful Shutdown を自前で制御できます。


2. WebSocket が真価を発揮する場面

  1. リアルタイム双方向同期

    • チャット、共同ドキュメント編集、オンラインホワイトボード。
  2. 高頻度 Push

    • 株価ティッカー、IoT センサストリーム、ゲーム状態のブロードキャスト。
  3. 近似リアルタイムで大量接続

    • ライブメトリクス監視、大規模スポーツ配信のコメントストリーム。

「サーバ → クライアント片方向」かつ接続数が限定的なら SSE(Server-Sent Events)、順序保証より低遅延を優先する UDP 系なら WebTransport/Datagram の検討がベターです。要件が“低レイテンシ双方向”なら WebSocket 一択と覚えてください。


3. FastAPI + Uvicorn で組むミニチャット

Python 3.11/FastAPI 0.110 系で動作確認

# app.py
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from collections import defaultdict
import asyncio, json, uuid

app = FastAPI(title="Realtime Chat over WebSocket")
rooms: dict[str, set[WebSocket]] = defaultdict(set)

async def broadcast(room: str, payload: dict):
    dead = set()
    msg = json.dumps(payload, ensure_ascii=False)
    for ws in rooms[room]:
        try:
            await ws.send_text(msg)
        except Exception:           # 送信失敗=切断
            dead.add(ws)
    rooms[room] -= dead

@app.websocket("/ws/{room}")
async def ws_endpoint(ws: WebSocket, room: str):
    await ws.accept()
    rooms[room].add(ws)
    cid = uuid.uuid4().hex[:8]

    try:
        await broadcast(room, {"sys": f"🟢 {cid} joined"})
        while True:
            txt = await asyncio.wait_for(ws.receive_text(), timeout=30)
            await broadcast(room, {"user": cid, "msg": txt})
    except (WebSocketDisconnect, asyncio.TimeoutError):
        await broadcast(room, {"sys": f"🔴 {cid} left"})
    finally:
        rooms[room].discard(ws)
        if not rooms[room]:
            rooms.pop(room, None)
pip install fastapi "uvicorn[standard]"
uvicorn app:app --host 0.0.0.0 --port 8000
  • Ping/Ponguvicorn --ws ping-interval 20 --ws ping-timeout 10 で自動化。
  • 水平スケール時は rooms を Redis PUB/SUB や NATS に置換し、Pod 間でブロードキャストを共有。

4. HTTP(Web API) との対比

観点 REST/GraphQL over HTTP WebSocket
接続モデル リクエスト → レスポンス/ステートレス 常時接続フルデュープレックス
オーバーヘッド TLS + ヘッダを毎回 初回 Upgrade 後は数 byte
負荷分散 コネクション短命で容易 Sticky 必須 or 外部 Pub/Sub
キャッシュ CDN/SWR 等が有効 基本不可
セキュリティ機構 CORS, CSRF, OAuth 充実 Origin 検証と TLS のみ。再認証不可
代表ユースケース CRUD API, バッチ, 公開エンドポイント チャット, ゲーム同期, ティッカー

REST API が“短命 & ステートレス”でスケールを担保するのに対し、WebSocket は“長寿命 & 状態保持”でリアルタイムを達成します。


5. 実装・運用時の留意点

  1. 接続ライフサイクル

    • ping_interval 15–30 s / ping_timeout 5–10 s で死活を速断。
    • Close フレーム(1000,1001,1008) → 相手の Close 受信 → TCP FIN の順で Graceful。
  2. バックプレッシャ

    • asyncio.Queue 送信バッファに上限を設け、溢れたらドロップ or 切断。
    • 1 接続あたりのメモリと FD を監視 (ulimit -n)。
  3. スケールアウト

    • Sticky session が効かない LB では Redis Stream/NATS でメッセージをファンアウトし、サーバをステートレス化。
    • AWS ALB の Idle Timeout を Ping 間隔の 2–3 倍に設定。
  4. セキュリティ

    • wss:// 強制、握手時に Cookie or JWT を検証。
    • 接続中に権限が変わる可能性がある場合は セッション寿命を短くして再接続で更新。
    • permessage-deflate は CPU DoS を誘発するため max_window_bits=15 など制限を入れる。
  5. クライアント再接続戦略

    • 指数バックオフ + ジッター (1s,2s,4s…max30s)。
    • Close Code 1006/1011 は UI に「一時的障害」と表示し自動再試行。
  6. モニタリング

    • websocket_open_connectionsping_rtt_secondssend_queue_size を Prometheus で収集。
    • 異常時に Cut-off しても “サービス全体が詰まらない” 設計を徹底。

6. まとめ

WebSocket は「低レイテンシ × 双方向 × ブラウザ互換」を満たす唯一の標準プロトコルであり、HTTP エコシステムの枠を超えたリアルタイム機能を提供します。その一方で 接続維持によるリソース圧迫既存 HTTP セキュリティモデルの不在 がトレードオフです。
上級エンジニアが WebSocket を採用する際は、

  1. 要件が本当に双方向か?
  2. 水平スケールの出口(Pub/Sub)を用意したか?
  3. Ping/Pong, Close, 再接続, Back-pressure をコードレベルで実装したか?

をチェックリストに据え、プロトコルの特性と運用コストを天秤に掛けましょう。この記事が設計・実装・運用の各フェーズでの指針となれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?