SYSTEM DESIGN GUIDE — PART 2 / 7
ネットワークと通信
IP・TCP・UDP・QUIC からロードバランサ、API 設計、リアルタイム通信まで。データがユーザーに届くまでの経路を、レイヤーごとに分解します。
- ① 導入と土台
- ② ネットワークと通信
- ③ データを保存する
- ④ システムの部品箱
- ⑤ 壊れないシステム
- ⑥ アーキテクチャの型
- ⑦ 面接を突破する
このページの内容
- CH 5インターネットの基礎(IP / TCP / UDP / QUIC)
- CH 6HTTP と TLS
- CH 7DNS
- CH 8ロードバランサとプロキシ
- CH 9API 設計
- CH 10リアルタイム通信
CHAPTER 05インターネットの基礎(IP / TCP / UDP / QUIC)
「リクエストがサーバーに届くまで」を層ごとに説明できるようになる。
5.1レイヤーの地図
┌──────────────────────────────────────────┐ │ L7 アプリケーション HTTP, gRPC, DNS, SMTP │ ← 何を伝えるか ├──────────────────────────────────────────┤ │ L4 トランスポート TCP, UDP, QUIC │ ← 確実に届けるか ├──────────────────────────────────────────┤ │ L3 ネットワーク IP, ICMP, BGP │ ← どの経路で運ぶか ├──────────────────────────────────────────┤ │ L2 データリンク Ethernet, ARP │ ← 隣の機器へ渡す ├──────────────────────────────────────────┤ │ L1 物理 光ファイバ, 電波 │ ← 物理的な信号 └──────────────────────────────────────────┘
手紙の配達です。
- L7 = 手紙の中身(日本語で書かれた文章)
- L4 = 書留にするか普通郵便にするか(届いたか確認するかどうか)
- L3 = 宛先住所と配送経路
- L2 = 今この郵便局から隣の郵便局へトラックで運ぶ
- L1 = トラックそのもの
システムデザインで直接扱うのは主に L4 と L7、そしてルーティングの L3 です。
5.2IP — 住所と経路
- IP アドレス:v4 は 32 bit(例
93.184.216.34)、v6 は 128 bit - IP はベストエフォート。届かないことも、順序が入れ替わることも、重複することもある
- プライベート IP:
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16は社内・VPC 用 - CIDR 記法:
10.0.1.0/24は「上位 24 bit が固定=256 個のアドレス」
アナキャスト (Anycast):同じ IP アドレスを世界中の複数拠点に割り当て、BGP のルーティングで「最も近い」拠点に届ける技術。CDN と DNS(8.8.8.8 など)の基盤技術であり、DDoS 分散にも使われます。Google の Maglev ロードバランサもこの上に構築されています。
5.3TCP — 確実に順番通り届ける
TCP が提供するもの
- 信頼性:ACK と再送により、失われたデータを届け直す
- 順序保証:シーケンス番号で並べ直す
- フロー制御:受信側が処理しきれない量を送らない(受信ウィンドウ)
- 輻輳制御:ネットワーク全体が詰まらないよう送信量を調整
クライアント サーバー
│ ── SYN ────────► │
│ ◄──── SYN+ACK ── │ ここまでで 1 RTT かかる
│ ── ACK ────────► │
│ ── データ ─────► │
接続確立に 1 RTT、TLS を足すとさらに 1〜2 RTT。東京〜米国(RTT 120 ms)だと、データを 1 バイトも送る前に 250〜360 ms 消える。これが「接続を使い回す(Keep-Alive / コネクションプール)」が重要な理由です。
輻輳制御アルゴリズム
| 名前 | 特徴 |
|---|---|
| Reno / CUBIC | パケットロスを輻輳の合図とする。Linux 既定は CUBIC |
| BBR(Google 開発) | 帯域と RTT を推定して制御。ロスの多い経路で大幅に高速。YouTube で採用 |
スロースタート:接続直後は少しずつしか送れません(初期輻輳ウィンドウ 10 パケット ≒ 14 KB)。つまり新しい接続では最初の 14 KB だけが 1 RTT で届く。「重要な CSS は 14 KB 以内に」という Web 最適化の格言はここから来ています。
ヘッドオブラインブロッキング(TCP レベル):パケット 3 が失われると、パケット 4, 5 が届いていてもアプリケーションには渡されません。順序保証の代償です。
5.4UDP — 速いが保証しない
- 送りっぱなし。順序も到達も保証しない。ハンドシェイクなし(0 RTT で送れる)
- ヘッダが小さい(8 B、TCP は 20 B〜)
UDP が適する場面
- DNS(1 往復で終わる、失敗したら再送すればいい)
- 音声・ビデオ通話(古いフレームが届いても意味がない。落とした方が良い)
- ゲーム(最新の位置情報だけが重要)
- メトリクス送信(StatsD など。多少落ちても統計的に問題ない)
5.5QUIC / HTTP/3 — TCP の欠点を修正する
QUIC は UDP の上に、TCP 相当の信頼性 + TLS 1.3 を再実装したものです。
| 課題 | TCP + TLS | QUIC |
|---|---|---|
| 接続確立 | TCP 1 RTT + TLS の版・再開方式に依存 | 通常 1 RTT、再接続や 0-RTT は条件とリプレイリスクを確認 |
| HOL ブロッキング | ストリーム間で発生 | ストリームごとに独立して解消 |
| 接続の識別 | 4 タプル(IP が変わると切れる) | Connection ID(Wi-Fi ↔ 4G 切替でも継続) |
| 実装場所 | OS カーネル(更新が遅い) | ユーザー空間(アプリ更新で改善できる) |
「モバイルユーザーが多く、ネットワーク切替が頻繁なので HTTP/3 (QUIC) を選びます。 Connection ID により Wi-Fi と LTE の切替で接続が切れず、パケットロスの多い環境でも ストリーム単位の独立により他のリソース取得がブロックされません。」
第 5 章 一問一答
- TCP 接続確立に何 RTT かかるか。
- 1 RTT(3 ウェイハンドシェイク)。TLS 1.3 を足すと計 2 RTT。
- UDP を選ぶ典型例を 3 つ。
- DNS、リアルタイム音声・映像、メトリクス。
- QUIC の最大の利点は。
- ストリーム単位での HOL ブロッキング解消、接続確立の高速化、接続 ID による移動耐性。
CHAPTER 06HTTP と TLS
HTTP のバージョン差、キャッシュ制御、TLS ハンドシェイクを説明できる。
6.1HTTP の基本
リクエスト レスポンス
GET /users/42 HTTP/1.1 HTTP/1.1 200 OK
Host: api.example.com Content-Type: application/json
Authorization: Bearer xxx Cache-Control: max-age=60
Accept: application/json ETag: "a1b2c3"
{"id":42,"name":"Kota"}
メソッドの性質(面接で頻出)
| メソッド | 安全 (safe) | 冪等 (idempotent) | 意味 |
|---|---|---|---|
| GET | ✅ | ✅ | 取得。副作用なし |
| HEAD | ✅ | ✅ | ヘッダのみ取得 |
| PUT | ❌ | ✅ | 完全置換(何回やっても同じ状態) |
| DELETE | ❌ | ✅ | 削除(2 回目は既に無い=同じ状態) |
| POST | ❌ | ❌ | 作成・任意の処理(2 回送ると 2 個できる) |
| PATCH | ❌ | ❌※ | 部分更新(設計次第で冪等にできる) |
冪等 (idempotent):同じ操作を何回実行しても、結果の状態が 1 回のときと同じであること。分散システムではリトライが避けられないので、冪等性は最重要概念です(第 18 章で詳述)。
ステータスコード(覚えるべきもの)
| コード | 意味 | 使いどころ |
|---|---|---|
| 200 / 201 / 204 | OK / 作成した / 中身なし | — |
| 301 / 302 / 304 | 恒久リダイレクト / 一時リダイレクト / 未更新 | 304 はキャッシュ検証で必須 |
| 400 / 401 / 403 / 404 | 不正な要求 / 未認証 / 権限なし / 存在しない | 401 と 403 の違いは頻出 |
| 409 / 412 / 422 | 競合 / 事前条件失敗 / 内容が不正 | 楽観ロックで 409, 412 を使う |
| 429 | レート制限超過 | Retry-After ヘッダを付ける |
| 500 / 502 / 503 / 504 | サーバー内部エラー / 上流不正 / 過負荷・停止中 / 上流タイムアウト | 503 と 504 の区別は運用で重要 |
リトライ可否はステータスコードだけでは決まりません。HTTP メソッドの冪等性、冪等性キーの有無、サーバーが処理を受け付けたか不明な状態、Retry-After、リトライ予算を合わせて判断します。500 でも一時障害なら再試行でき、502/503/504 でも非冪等な副作用を無条件に再送してはいけません。400 番台も 429 のように時間を置けば成功するものがあります。
6.2HTTP のバージョン
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| 下位層 | TCP | TCP | QUIC (UDP) |
| 多重化 | △(通常は keep-alive で順番に処理。パイプラインは普及しなかった) | ✅ 1 接続で多重化 | ✅ |
| ヘッダ圧縮 | ❌(テキスト) | ✅ HPACK | ✅ QPACK |
| サーバープッシュ | ❌ | ✅(ただし現在は非推奨) | ✅ |
| HOL ブロッキング | アプリ層で発生 | TCP 層で発生 | 解消 |
| バイナリ | テキスト | バイナリ | バイナリ |
gRPC は HTTP/2 の上に構築されており、多重化・双方向ストリーミングをそのまま活かします。
6.3HTTP キャッシュ(設計で必ず使う)
Cache-Control: public, max-age=3600, stale-while-revalidate=86400 ETag: "v3-a1b2c3" Last-Modified: Wed, 21 Aug 2026 07:28:00 GMT
| ディレクティブ | 意味 |
|---|---|
public / private |
CDN でキャッシュ可 / ブラウザのみ可(ユーザー固有データは必ず private) |
max-age=N |
N 秒間は再検証せず使ってよい |
s-maxage=N |
CDN 用の max-age(ブラウザとは別に設定できる) |
no-cache |
キャッシュはするが毎回サーバーに検証する(「キャッシュしない」ではない!) |
no-store |
一切保存しない(本当の意味でのキャッシュ禁止) |
immutable |
絶対に変わらない(ハッシュ付きファイル名に付ける) |
stale-while-revalidate=N |
期限切れでも N 秒はそれを返しつつ裏で更新 |
条件付きリクエスト(帯域を大幅に削減)
クライアント: GET /a.js If-None-Match: "v3-a1b2c3" サーバー: 304 Not Modified (ボディを送らない = 数十バイトで済む)
静的アセットの定石:ファイル名にコンテンツハッシュを埋め込み(app.9f2c1a.js)、Cache-Control: max-age=31536000, immutable を付ける。更新時はファイル名が変わるので、キャッシュ無効化が不要になります。
6.4TLS — 通信の暗号化
TLS が保証する 3 つのこと
- 機密性:盗聴されない(対称鍵暗号 AES-GCM / ChaCha20)
- 完全性:改竄されない(AEAD / MAC)
- 認証:相手が本物である(証明書チェーン)
Client Server │─ ClientHello (鍵共有材料, 対応暗号) ──►│ │◄─ ServerHello + 証明書 + Finished ────│ ← ここで鍵が確定 │─ Finished ─────────────────────────►│ │═══ 暗号化された HTTP ══════════════►│
TLS 1.2 は 2 RTT。TLS 1.3 は 1 RTT で、再接続時は 0-RTT も可能(ただし 0-RTT データはリプレイ攻撃の余地があるため、冪等な GET のみに使う)。
証明書の仕組み
ルート CA(ブラウザに埋め込み済み)
└ 中間 CA
└ サーバー証明書(example.com、公開鍵、有効期限、署名)
サーバーは秘密鍵を持ち、証明書の公開鍵と対応することを署名で示します。
知っておくと差がつく用語
- SNI (Server Name Indication):1 つの IP で複数ドメインの TLS を扱うための拡張。ハンドシェイクの平文部分にホスト名が乗るため、ECH(Encrypted Client Hello)で隠す動きがある
- mTLS(相互 TLS):クライアントも証明書を出す。サービス間通信の認証(ゼロトラスト)で標準的
- 証明書ピンニング:モバイルアプリで特定の証明書のみ信頼する。中間者攻撃対策だが運用が硬直する
- TLS 終端:ロードバランサで復号し、内部は平文 or 内部 mTLS。CPU 負荷の集約が目的
「外部からの TLS は L7 ロードバランサで終端し、 内部のサービス間通信は mTLS で相互認証します。証明書はサービスメッシュ(Envoy 等)が 自動で配布・ローテーションする構成にします。」
第 6 章 一問一答
- POST と PUT の最大の違いは。
- PUT は冪等、POST は冪等でない。
no-cacheの正しい意味は。- キャッシュはするが、使用前に必ずサーバーへ再検証する。
- リトライ判断で見るものは。
- ステータスコードだけでなく、冪等性、処理済みか不明か、Retry-After、リトライ予算、接続・タイムアウトの種類を組み合わせる。
- TLS 1.3 のハンドシェイクは何 RTT か。
- 1 RTT(再接続時 0-RTT も可)。
CHAPTER 07DNS
ドメイン名が IP に変わる過程と、DNS を設計に活用する方法を理解する。
7.1名前解決の流れ
ブラウザ │ ① ブラウザキャッシュ → OS キャッシュ → hosts ファイル ▼ リゾルバ(ISP or 8.8.8.8 / 1.1.1.1) │ ② キャッシュになければ再帰的に問い合わせ ├──► ルートサーバー (.) 「.com は c.gtld-servers.net に聞け」 ├──► TLD サーバー (.com) 「example.com は ns1.example.com に聞け」 └──► 権威サーバー (example.com) 「A レコードは 93.184.216.34 だ」 ▼ IP アドレスが返る(TTL 付き)
キャッシュがあれば 0〜数 ms、なければ 20〜200 ms。初回接続の体感速度に効くので、dns-prefetch や接続の事前確立(preconnect)が使われます。
7.2レコードの種類
| 種類 | 内容 | 用途 |
|---|---|---|
| A / AAAA | IPv4 / IPv6 アドレス | 基本 |
| CNAME | 別名(www → example.com) |
ルートドメインには使えない(ALIAS/ANAME で代替) |
| NS | 権威サーバーの指定 | 委譲 |
| MX | メールサーバー | メール |
| TXT | 任意文字列 | SPF/DKIM、ドメイン所有証明 |
| SRV | サービスのホストとポート | 内部サービス検出 |
| CAA | 証明書を発行できる CA の制限 | セキュリティ |
7.3DNS をシステム設計に使う
1. グローバル負荷分散 (GSLB)
- 地理ベース:日本からのクエリには東京の IP、欧州からは frankfurt の IP を返す
- レイテンシベース:実測 RTT が最小のリージョンを返す
- 加重ラウンドロビン:新バージョンに 5% だけトラフィックを流す(カナリア)
- ヘルスチェック連動フェイルオーバー:落ちたリージョンの IP を返さなくする
2. サービスディスカバリ
Kubernetes 内部では service-name.namespace.svc.cluster.local で解決します。
DNS フェイルオーバーの限界。TTL を 60 秒にしても、TTL を無視するリゾルバやアプリがある(Java の旧 JVM は無期限キャッシュがデフォルトだった)。そのため「DNS だけで即座に切り替わる」ことは期待してはいけません。秒単位の切替が必要なら Anycast + ロードバランサを使います。
7.4TTL の設計
| TTL | 用途 |
|---|---|
| 30〜60 秒 | フェイルオーバー対象、頻繁に変わるもの(ただし DNS 負荷とレイテンシが増える) |
| 300〜3600 秒 | 一般的なサービス |
| 86400 秒 | ほぼ変わらないもの(MX, NS) |
移行時の定石:切替の 24〜48 時間前に TTL を 60 秒に下げておき、切替後に戻す。
第 7 章 一問一答
- ルートドメインに CNAME を設定できない理由は。
- RFC 上 CNAME は他のレコードと共存できず、ルートには SOA/NS が必須のため。
- DNS でフェイルオーバーする際の注意点は。
- TTL を無視するクライアントがあり、切替が即時に完了しない。
- 地理的に近いサーバーへ振り分ける方法を 2 つ。
- GeoDNS(DNS で近い IP を返す)と Anycast(同一 IP を経路で最寄りへ)。
CHAPTER 08ロードバランサとプロキシ
L4/L7 の違い、分散アルゴリズム、ヘルスチェックの設計を説明できる。
8.1なぜロードバランサが要るのか
- 負荷分散:複数台に均等に配る
- 可用性:落ちたサーバーを外す
- 抽象化:クライアントは 1 つの IP/ホスト名だけ知っていればよい
- 付加機能:TLS 終端、圧縮、レート制限、WAF、リトライ、認証
8.2L4 と L7
| L4(トランスポート) | L7(アプリケーション) | |
|---|---|---|
| 見るもの | IP アドレスとポート | HTTP ヘッダ、パス、Cookie、メソッド |
| できること | TCP/UDP を素通しで転送 | パスによるルーティング、TLS 終端、リトライ、キャッシュ、圧縮 |
| 速度 | 非常に速い(数百万 pps) | 遅い(数万〜数十万 rps) |
| 例 | AWS NLB, LVS, Google Maglev | AWS ALB, Nginx, Envoy, HAProxy, Google GFE |
L4 は「封筒の宛先だけ見て仕分ける郵便局」、L7 は「封を開けて中身を読み、内容に応じて適切な部署へ回す受付」。
Google の実際の構成。外部からのトラフィックは Maglev(L4, ソフトウェアで ECMP + コンシステントハッシュ) → GFE(L7, TLS 終端) → バックエンドサービス、という 2 段構成になっています。
8.3分散アルゴリズム
| アルゴリズム | 説明 | 向き・不向き |
|---|---|---|
| ラウンドロビン | 順番に配る | 単純。処理時間がバラバラだと偏る |
| 加重ラウンドロビン | 性能比で配分 | 異なるスペックの混在時 |
| 最小コネクション数 | 今一番暇な所へ | 処理時間がばらつく場合に有効 |
| 最小応答時間 | 実測が速い所へ | 良いが、指標が不安定になりがち |
| IP ハッシュ / コンシステントハッシュ | 同じキーは同じサーバーへ | キャッシュのヒット率を上げたいとき必須 |
| Power of Two Choices | ランダムに 2 台選び、空いている方へ | 少ない観測で偏りを減らせる。負荷指標とワークロードに依存 |
Power of Two Choices (P2C) は覚えておく価値が非常に高いです。完全ランダムだと最大負荷は O(log n / log log n) ですが、2 台から選ぶだけで O(log log n) に落ちます。実装も簡単で状態共有が不要。Nginx, Envoy, HAProxy などが採用しています。
8.4ヘルスチェック
| 種類 | 説明 |
|---|---|
| Liveness(生存) | プロセスが生きているか。失敗 → 再起動 |
| Readiness(受付可能) | トラフィックを受けられるか。失敗 → LB から外す(再起動はしない) |
| Startup(起動) | 起動中の猶予。JVM のような遅い起動に必要 |
Readiness の中で DB への疎通確認をすると、DB が一瞬詰まった瞬間に全サーバーが同時に LB から外れ、サービス全断になります。→ 依存先のチェックは慎重に。「自分自身が動くか」だけを見るのが基本。
ヘルスチェックが軽すぎる(return 200 するだけ)と、実際は壊れているサーバーにトラフィックが流れ続けます。→ ディープヘルスチェックは監視・アラート用、シャローは LB 用と役割を分ける。
8.5その他のプロキシ
| 種類 | 位置 | 目的 |
|---|---|---|
| リバースプロキシ | サーバー側 | 負荷分散、TLS 終端、キャッシュ、隠蔽 |
| フォワードプロキシ | クライアント側 | 社内からの外向き通信の制御・監査 |
| API ゲートウェイ | 入口 | 認証、レート制限、ルーティング、集約、変換 |
| サイドカープロキシ | 各 Pod の隣 | サービスメッシュ(mTLS、リトライ、可観測性)。例: Envoy |
「入口は L4 LB で TCP を分散し、その後段の L7 プロキシで TLS 終端・パスベースルーティング・レート制限を行います。 L7 の状態を持たせないことで、L7 レイヤ自体も水平にスケールできます。」
第 8 章 一問一答
- L4 と L7 のどちらが速いか、なぜか。
- L4。パケットを解析せず転送するだけだから。
- Readiness と Liveness の違いは。
- Readiness 失敗は LB から外す、Liveness 失敗は再起動する。
- キャッシュサーバー群への振り分けに適したアルゴリズムは。
- コンシステントハッシュ(同じキーが同じノードに行きヒット率が上がる)。
- Power of Two Choices の利点は。
- 全体状態を知らずに、ランダム 2 台比較だけで負荷の偏りを劇的に減らせる。
CHAPTER 09API 設計
面接で 3 分で API を定義でき、その設計判断を説明できる。
9.13 つのスタイル
| REST | RPC / gRPC | GraphQL | |
|---|---|---|---|
| 発想 | リソース(名詞)を URL で表す | 手続き(動詞)を呼ぶ | クライアントが必要な形を問い合わせる |
| 形式 | JSON over HTTP | Protobuf over HTTP/2 | クエリ言語 |
| 得意 | 公開 API、キャッシュしやすい | サービス間通信(速い・型安全・ストリーミング) | 画面ごとに必要データが違うクライアント |
| 苦手 | 過剰取得/不足取得(over/under-fetching) | ブラウザから直接呼びにくい(gRPC-Web が必要) | キャッシュ困難、N+1、複雑なクエリのコスト制御 |
定番の答え
「クライアント(モバイル・Web)向けの公開 API は REST + JSON にします。HTTP キャッシュと CDN が効き、デバッグしやすいためです。内部のサービス間通信は gRPC にします。 Protobuf によるスキーマ定義でバージョン互換を担保でき、HTTP/2 の多重化で コネクション効率が良く、ペイロードも小さいためです。」
9.2REST の設計原則
GET /v1/users 一覧
POST /v1/users 作成
GET /v1/users/{id} 取得
PATCH /v1/users/{id} 部分更新
DELETE /v1/users/{id} 削除
GET /v1/users/{id}/posts サブリソース
POST /v1/videos/{id}:publish ← 動詞が必要な場合はカスタムメソッド(Google API 設計ガイド流)
原則
- URL は名詞・複数形、階層は 2 段まで
- 動詞は HTTP メソッドで表す
- バージョンを URL かヘッダに入れる(
/v1/,Accept: application/vnd.x.v1+json) - フィルタ・ソート・ページングはクエリパラメータ
9.3ページネーション(面接頻出)
❌ オフセット方式
GET /posts?offset=100000&limit=20
- 問題 1:DB は 100,020 行読んで 100,000 行捨てる → 深いページほど激遅
- 問題 2:読んでいる間に新規投稿が入ると重複・欠落する
✅ カーソル(キーセット)方式
GET /posts?after=eyJpZCI6MTIzLCJ0cyI6MTY5OTk5fQ&limit=20 → WHERE (created_at, id) < (:ts, :id) ORDER BY created_at DESC, id DESC LIMIT 20
- インデックスで一発シーク → 深いページでも概ね安定したコスト
created_atとidのような、安定して一意な複合ソートキーを使う- 新規挿入、更新、削除をまたぐ完全なスナップショットが必要なら、スナップショット境界もカーソルに含める
- カーソルは署名付きの不透明トークンにし、クエリ条件・テナント・有効期限と結び付ける。Base64 は暗号化でも安全性でもない
{
"items": [ ... ],
"next_cursor": "eyJpZCI6...", // null なら終端
"has_more": true
}
総件数(total)は、データベース、インデックス、スナップショット、事前集計の有無でコストが変わります。厳密な COUNT が高い場合は、概算、非同期集計、あるいは返さない設計を選びます。
9.4冪等性キー(Idempotency Key)
決済のような「二重実行が致命的」な POST に必須のパターン。
POST /v1/payments
Idempotency-Key: 8f14e45f-ea... ← クライアントが生成する UUID
{"amount": 1000, "currency": "JPY"}
サーバー側の処理
1. キー、リクエストハッシュ、状態(in_progress / succeeded / failed)、レスポンスを保存 2. 既存キーが succeeded なら、同じリクエストか検証して保存済みレスポンスを返す 3. in_progress なら同時実行を抑止し、ポーリングまたは再試行可能な応答を返す 4. 新規キーは一意制約とトランザクションで claim し、副作用と結果保存を安全に結び付ける 5. タイムアウトやクラッシュ後は、lease の期限切れ、照合、再実行・照会の方針を定義する 6. 保持期間は業務上の再送期間と、プロバイダの仕様に合わせて決める
リクエスト内容のハッシュも保存し、同じキーで異なる内容が来たら 422 を返すのが正しい実装です。
9.5その他の必須トピック
エラー形式(統一する)
{
"error": {
"code": "RESOURCE_EXHAUSTED",
"message": "Rate limit exceeded. Retry after 30s.",
"details": [{"@type": "RetryInfo", "retry_delay": "30s"}],
"request_id": "req_01H..."
}
}
request_id を必ず返すこと。ユーザーからの問い合わせで、ログを一発で引けます。
バージョニングと互換性
- 後方互換な変更(OK):フィールドの追加、任意パラメータの追加
- 破壊的変更(NG):フィールドの削除・改名、型変更、必須化、意味の変更
- Protobuf ではフィールド番号を絶対に再利用しない(
reservedを使う)
バルク / バッチ API
POST /v1/users:batchGet で N+1 を潰す。部分成功を許すなら、各要素にステータスを持たせる。
ロングランニング操作
POST /v1/videos:transcode → 202 Accepted, {"operation_id": "op_123"}
GET /v1/operations/op_123 → {"done": false, "progress": 0.4}
第 9 章 一問一答
- オフセットページネーションの 2 つの問題は。
- 深いページで性能劣化、データ挿入時の重複・欠落。
- 内部サービス間通信に gRPC を選ぶ理由を 3 つ。
- Protobuf による型安全とスキーマ進化、HTTP/2 多重化、ペイロードが小さく高速。
- 決済 API を二重実行から守る方法は。
- クライアント生成の冪等性キー、リクエストハッシュ、状態機械、同時実行 claim、結果照会、失敗・タイムアウト後の再試行方針を組み合わせる。
CHAPTER 10リアルタイム通信
「サーバーからクライアントへ push したい」ときの選択肢を比較できる。
10.14 つの方式
| 方式 | 仕組み | 遅延 | サーバー負荷 | 使いどころ |
|---|---|---|---|---|
| ショートポーリング | 5 秒ごとに GET | 最大 5 秒 | 高(無駄なリクエスト多数) | 更新頻度が低く実装を単純にしたい場合 |
| ロングポーリング | リクエストを保持し、更新時に返す | 低 | 中(接続保持) | WebSocket が使えない環境の代替 |
| SSE (Server-Sent Events) | HTTP で一方向のストリーム | 低 | 中 | 通知、株価、進捗、LLM のトークン出力 |
| WebSocket | 双方向の永続接続 | 最低 | 中〜高 | チャット、ゲーム、共同編集 |
- ショートポーリング = 「もう届いた?」と 5 秒おきに郵便受けを見に行く
- ロングポーリング = 郵便受けの前で届くまで待ち、届いたら家に戻ってまた出てくる
- SSE = 郵便配達員が家まで持ってきてくれる(一方通行)
- WebSocket = 配達員と直通の電話回線を引く(双方向)
10.2選択の基準
サーバー → クライアントの一方向で十分?
├─ YES → SSE を選ぶ(HTTP なので LB・プロキシと相性が良く、自動再接続も仕様にある)
└─ NO(双方向・低遅延が必要)→ WebSocket
└─ 極端な低遅延・多少の欠損を許容(ゲーム)→ WebRTC / QUIC datagram
SSE の制約。HTTP/1.1 ではブラウザの同一ドメイン接続数(6)を消費します。HTTP/2 以降なら多重化されるので問題ありません。
10.3WebSocket を大規模に運用する設計
これは面接(チャットシステム設計)で必ず問われます。
┌── 接続サーバー #1 (10 万接続) ──┐
クライアント ──│ │
├── 接続サーバー #2 (10 万接続) ──┤
└── 接続サーバー #3 (10 万接続) ──┘
│
「誰がどのサーバーに繋がっているか」
▼
┌────────────────────────┐
│ セッションレジストリ │ Redis: user_id → server_id
└────────────────────────┘
│
┌────────────────────────┐
│ Pub/Sub(Redis/Kafka) │ サーバー間でメッセージを中継
└────────────────────────┘
設計上の論点
- 接続数の上限:1 台で 10 万〜100 万接続(C10M)。制約はファイルディスクリプタ数、メモリ(接続あたり数 KB〜数十 KB)、エフェメラルポート(サーバー側は宛先ポート固定なので上限は FD 側)
- ルーティング:ユーザー A → B に送るには、B がどのサーバーにいるか知る必要がある → レジストリ(Redis)or 全サーバーへの Pub/Sub ブロードキャスト
- ハートビート:NAT やロードバランサのアイドルタイムアウト(典型 60 秒)で接続が黙って切れる → 30 秒ごとの ping/pong 必須
- 再接続:指数バックオフ + ジッタ。再接続時は「最後に受け取った ID 以降」を取得して欠落を埋める(=オフライン中のメッセージ配信)
- デプロイ:全サーバーを同時に再起動すると全クライアントが同時に再接続(サンダリングハード) → 段階的にドレインし、再接続にジッタを入れる
加点ポイント
「WebSocket の接続は状態を持つので、接続サーバー層とビジネスロジック層を分離します。 接続サーバーは『接続の保持とメッセージの中継』だけに責任を限定し、 ユーザー ID → 接続サーバーのマッピングを Redis に持ちます。 これにより、ロジック側を自由にデプロイしても接続が切れません。」
第 10 章 一問一答
- 通知配信に SSE を選ぶ理由は。
- 一方向で十分、HTTP なので既存インフラと相性が良く、自動再接続が仕様に含まれる。
- WebSocket でハートビートが必要な理由は。
- NAT/LB のアイドルタイムアウトによる無通知切断を検知・防止するため。
- 数千万接続をどう捌くか。
- 接続サーバー層を水平分割し、ユーザー→サーバーのマッピングを外部レジストリに持ち、Pub/Sub で中継する。


