HTTP/1.1の「呪縛」:HOLブロッキングという名の渋滞を紐解く
エンジニアの皆さん、お疲れ様です。インフラの現場で「なぜかレスポンスが遅い」「パケットキャプチャを見ると、特定の通信だけが不自然に待たされている」という怪奇現象に遭遇したことはありませんか?
Web APIの設計やインフラ運用に携わっていると、最新のHTTP/3やQUICの華やかな話題に目が行きがちです。しかし、我々の足元を支えるHTTP/1.1には、依然として解決しがたい「構造的な渋滞」が埋め込まれています。それが今回深掘りするHOL(Head-of-Line)ブロッキングです。
これは単なる「遅延」ではありません。HTTP/1.1の通信規約そのものが抱える「律儀すぎるがゆえの悲劇」なのです。
—
1. なぜ「先頭の詰まり」が起きるのか?
HTTP/1.1のパイプライン化(Pipelining)は、理論上は画期的な機能でした。クライアントはレスポンスを待たずに次々とリクエストを送信できる。これによってRTT(往復遅延時間)を削減できるはずでした。
しかし、ここには重大な制約があります。「サーバーは受信した順番通りにレスポンスを返さなければならない」というRFC 2616(後の7230)のルールです。
悲劇のシーケンス
1. リクエストA(重い画像データ)を送信。
2. リクエストB(軽いJSONデータ)を送信。
3. サーバーはリクエストAの処理に時間がかかっている間、リクエストBの処理が終わっていても、Aのレスポンスを送り終えるまでBを送信できません。
結果として、軽いリクエストBは、重いリクエストAの背後で「行列の先頭が動くのをじっと待つ」羽目になります。これがHOLブロッキングの正体です。
—
2. 実機で確認する:curlによる「待ち」の可視化
言葉で説明するよりも、実際にパケットを流してみるのが一番です。`curl`を使って、あえてパイプラインを意識した通信を行ってみましょう。
実際には多くのブラウザやサーバーでパイプライン化はデフォルトOFFですが、
挙動をシミュレートする概念コードです。
–http1.1: 強制的にHTTP/1.1を使用
-v: 詳細ログを表示して「待ち」が発生しているか確認
curl -v –http1.1 \
-H “Host: example.com” \
http://example.com/heavy-resource \
http://example.com/light-api
詳細ログを見ると、`Connection #0` が再利用されているにも関わらず、2つ目のリクエストに対するレスポンスが、1つ目の巨大なデータ転送が終わるまで「沈黙」していることが確認できるはずです。
—
3. インフラエンジニアが直面する「回避策」の代償
現場では、このHOLブロッキングを回避するために「ブラウザによるドメイン分割(ドメインシャーディング)」という荒技が使われてきました。
- `static1.example.com`
- `static2.example.com`
このようにホスト名を分けることで、ブラウザは「別のホストなら別のTCPコネクションを張ってもいい」というルールを悪用(あるいは活用)し、並列接続数を増やして渋滞を分散させます。
しかし、これはTCPの3ウェイハンドシェイクを何度も発生させるという、インフラ的に非常にコストの高い解決策です。TLSハンドシェイクのオーバーヘッドも重なり、モバイル回線のような不安定な環境では、逆にレイテンシを悪化させる要因にもなり得ます。
—
4. Web API設計者への教訓:HTTP/1.1とどう向き合うか
もし皆さんが今、HTTP/1.1ベースのAPIを設計しているなら、以下のプラクティスを意識してください。
① 「小さなリクエスト」を詰め込まない
APIの設計において、1つのエンドポイントに複数のリソースを強引に詰め込むのは避けましょう。HOLブロッキングの影響を最小化するために、リクエストとレスポンスのサイズを適切に分割することが肝要です。
② サーバーサイドでのコネクション管理
NginxなどのWebサーバーを設定する際、`keepalive`のタイムアウト設定は重要です。
nginx.conf の設定例
http {
# 接続を維持することでハンドシェイクのコストを減らす
keepalive_timeout 65;
# ただし、長すぎるとリソースを枯渇させるため、
# 負荷テストに基づいたチューニングが必須です。
}
③ HTTP/2, HTTP/3 への移行を「インフラの正攻法」とする
結局のところ、HTTP/1.1のHOLブロッキングはプロトコルの仕様による制約です。HTTP/2では「ストリーム」という概念を導入し、1つのTCPコネクション上で複数の通信を多重化(Multiplexing)することで、この問題を根本的に解決しました。
—
最後に:ネットワークを「俯瞰」する視点を持て
「なぜ遅いのか」を突き詰めると、多くの場合、ネットワークの物理的な制約や、HTTPのような古い規約の呪縛に行き当たります。
HOLブロッキングは、単なるバグではなく、当時のエンジニアたちが「いかに効率よくデータを送るか」を考え抜いた末の「制約」でした。その歴史的背景を知っているか否かで、障害発生時のトラブルシューティングの深さが全く変わります。
次回の記事では、HTTP/2のストリーム多重化が、いかにしてこの「渋滞」を解消し、また新たな課題(TCPレベルのHOLブロッキング)を生んだのかについて解説します。
現場からは以上です。引き続き、健やかなネットワークライフを。
コメント