ネットワーク●●●○○

HTTP/2

えいちてぃーてぃーぴーつー

1本のTCP接続に複数のやり取りを多重化し、HTTP/1.1の待ち行列問題を解いた改訂版。

概要

HTTP/2 は、Web の土台である HTTP を約15年ぶりに大改訂したプロトコルで、2015年に標準化されました。意味論(メソッドやステータスコード、ヘッダの意味)は HTTP/1.1 のまま変えず、「メッセージをどう運ぶか」という転送の仕組みだけを根本から作り直したのが特徴です。アプリケーション開発者から見ると同じ HTTP に見えるのに、裏側の運び方が別物になっている、と言えます。

最大の変更は「多重化(マルチプレクシング)」です。HTTP/1.1 では1本の接続の上で1度に1つのやり取りしかできませんでしたが、HTTP/2 では1本の TCP 接続の中に「ストリーム」と呼ばれる論理的な通り道を何本も作り、多数のリクエストとレスポンスを同時並行で流せます。現在では主要ブラウザと大半の大手サイトが対応しており、HTTPS でアクセスするサイトの多くは気づかないうちに HTTP/2 で通信しています。

なぜ生まれたか

HTTP/1.1 の基本は「1つの接続で、リクエストを送り、レスポンスを待ち、返ってきたら次を送る」という逐次の往復です。ところが Web ページはどんどんリッチになり、1ページの表示に画像・CSSJavaScript など数十から百以上のリソースを取得するのが当たり前になりました。逐次処理では、先頭のレスポンスが遅いと後続がすべて待たされます。これが HTTP レベルの「Head-of-Line(HOL)ブロッキング」、いわば隊列先頭の閉塞です。

ブラウザは対策として1ドメインあたり6本程度の接続を並列に張りますが、それでも足りず、開発者側は「画像サーバを img1 と img2 のサブドメインに分けて接続数の上限を稼ぐ」(ドメインシャーディング)、「小さい画像を1枚に結合する」(CSSスプライト)、「多数のJSファイルをバンドラで1つに固める」といった苦肉の策を積み上げていました。いずれもプロトコルの制約を回避するためだけの複雑さです。Google が実験プロトコル SPDY で「1本の接続に多重化すれば速くなる」ことを実証し、それを土台に IETF が標準化したのが HTTP/2 です。

詳細

バイナリフレーミングとストリーム

HTTP/1.1 のメッセージは人間が読めるテキストでしたが、HTTP/2 はすべてを「フレーム」というバイナリの小片に分解して運びます。ヘッダは HEADERS フレーム、本文は DATA フレームになり、各フレームには所属するストリームの番号が付きます。受信側は番号を頼りにフレームを組み立て直すので、複数のリクエスト・レスポンスのフレームを1本の接続上で自由に混ぜて送れます。これが多重化の正体です。テキストの解析より高速で曖昧さがないことも、バイナリ化の利点です。

HTTP/1.1HTTP/2TCP接続 1リクエストA → 応答A → リクエストB …TCP接続 21接続につき同時に1往復だけTCP接続 3〜6ドメインごとに約6本が上限先頭の応答が遅いと後続が全部待つHOLブロッキングTCP接続 1本だけストリーム1: HTMLストリーム3: CSSストリーム5: 画像 …同時に多数フレームを混ぜて1本で運ぶ=多重化
接続の使い方の対比 — HTTP/1.1 は接続を増やして並列化、HTTP/2 は1本に多重化

ヘッダ圧縮 HPACK と優先度

HTTP のヘッダは Cookie や User-Agent など毎回ほぼ同じ内容が繰り返し送られ、リクエスト数が多いほど無駄が積み上がります。HTTP/2 は HPACK という専用の圧縮方式を導入し、送信済みのヘッダを送受信の双方が「辞書」として記憶して、2回目以降は短い番号で参照します。これにより数百バイトのヘッダが数バイトに縮むこともあります。gzip のような汎用圧縮を使わない専用設計なのは、圧縮と秘密情報の混在を突く CRIME という攻撃への対策でもあります。

また、多重化したことで「どのストリームに帯域を優先して割り当てるか」という新しい問題が生まれ、優先度(プライオリティ)の仕組みが用意されました。レンダリングを妨げる CSS を画像より先に、といった制御が意図されましたが、実装のばらつきが大きく、後継仕様ではよりシンプルな方式に整理されています。同じく鳴り物入りだった「サーバプッシュ」(リクエストされる前にサーバがリソースを送りつける機能)も、キャッシュとの相性の悪さから効果が出しにくく、主要ブラウザでは削除されました。

残った限界 — TCPレベルのHOLブロッキング

HTTP/2 は HTTP レベルの待ち行列を解消しましたが、その下の TCP には「バイト列を順番どおりに届ける」という性質があります。1本の TCP に全ストリームを載せたため、パケットが1つ失われると、TCP が再送を待つ間、そのパケットと無関係なストリームのデータまで全部が足止めされます。つまり隊列閉塞が HTTP 層から TCP 層に移っただけ、という側面が残ったのです。パケットロスの多いモバイル回線では HTTP/1.1 の複数接続より遅くなるケースさえ報告されました。この限界が、TCP そのものを置き換える HTTP/3 の開発動機になります。

実務への影響 — 最適化の常識が変わった

HTTP/2 の普及は開発のベストプラクティスも変えました。接続数の節約が目的だったドメインシャーディングは、むしろ接続を分断してヘッダ圧縮や輻輳制御の効率を下げるため不要(むしろ有害)になりました。ファイル結合も「多数の小さいリクエストが安くなった」ことで必須ではなくなりましたが、圧縮効率やモジュール解決のオーバーヘッドの観点からバンドラ自体は今も使われており、その意義が「リクエスト数削減」から「変換と最適化」へ変質した、と捉えるのが正確です。

なお、仕様上 HTTP/2 は平文でも動きますが、主要ブラウザはすべて TLS 上でのみ対応しているため、事実上 HTTPS が前提です。導入自体はリバースプロキシCDN の設定で有効化するだけのことが多く、アプリケーションコードの変更はほぼ不要です。