HTTP / HTTP/2 / HTTP/3
1 本の接続でページのファイルを取得するとき、HTTP のバージョンによって接続の共有方法と損失時の待ち方が変わります。本例の HTTP/1.1 はリクエストを順に送り、HTTP/2 は 1 本の TCP バイトストリームで多重化し、HTTP/3 は QUIC のストリーム単位でデータを届けます。
同じ 4 ファイルのページを 3 回見ます。HTTP/1.1 は階段状に取得します。HTTP/2 は並行転送しますが、CSS を含む TCP パケットを失うと、到着済みの JavaScript と画像も待たされます。HTTP/3 で同じパケットを失うと、JavaScript と画像は完了し、CSS が待ちます。リクエストの待ち行列、TCP の Head-of-Line ブロッキング(HOL)、ストリーム単位の配送という違いです。
接続を共有する仕組み
条件は 1 オリジン、1 接続です。ブラウザーは HTTP/1.1 で同一オリジンに約 6 本の接続を開くことがありますが、上限は実装に依存します。まず 1 本の動きを比較します。
この HTTP/1.1 モデルは keep-alive を使い、パイプライン化しません。in_flight は最大 1 です。GET /style.css の 200 レスポンスが完了するまで /app.js を送りません。損失時に遅れるのは未送信の待ち行列です。HTTP/1.1 の仕様には、複数リクエストを先に送るパイプラインもありますが、レスポンスは順序どおりに返す必要があります。
HTTP/2 は 1 本の TCP 接続と TLS セッションを使い、ALPN で h2 を選びます。クライアント開始のストリーム ID は奇数で、/ が 1、残りが 3、5、7 です。フレームを交互に送れるため、ウォーターフォールの棒が重なります。ただし TCP のシーケンスは共通です。TCP はバイトを順序どおりにアプリケーションへ渡すため、CSS のデータに欠落があると、後続の JavaScript と画像が受信バッファーにあっても HTTP/2 へ渡せません。
HTTP/3 は UDP 上の QUIC を使い、TLS 1.3 をトランスポートに統合します。本例の新規接続は 1 RTT で、TCP と TLS を分けた 2 RTT より短くなります。配送順序は STREAM ID とオフセットで管理しますが、損失検出と輻輳制御は接続に属します。パケット番号はストリームごとではなく、Initial、Handshake、Application Data の 3 空間に分かれます。0-RTT と 1-RTT は最後の空間を共有します。CSS のストリーム 4 だけを含むパケットを失っても、8 と 12 は配送できます。
フロントエンドの fetch オプションで HTTP/3 を選ぶわけではありません。オリジンや CDN が Alt-Svc または HTTPS DNS レコードで対応を知らせ、ブラウザーがハンドシェイクで h3 を交渉します。Chrome DevTools → Network の Protocol 列で h3、h2、http/1.1 を確認し、Timing やウォーターフォールと比べられます。損失のある経路ではストリーム単位の配送が効きますが、実際の高速化を保証するものではありません。
HTTP バージョンのステップ
初期設定の Compare は /、/style.css、/app.js、/photo.jpg の 4 ファイルを 1 接続で取得します。接続の確立を示した後、損失なしの HTTP/1.1、CSS パケットを失う HTTP/2、同じ損失の HTTP/3 を比較します。
- 初期化。 4 ファイルを表示し、待機中なのは
/だけ。待ち行列、停止、分離という 3 つの挙動を示します。 - TCP。 3 ウェイハンドシェイクを 1 RTT にまとめ、接続行へ TCP ブロックを追加します。詳細は TCP Handshake の教材で扱います。
- TLS 1.3。 さらに 1 RTT。まだ HTTP は送っていません。接続の確立で 2 列を使います。
- GET /。
200が返り、rtt = 3、done = 1/4。 - HTML を解析。 残り 3 ファイルを発見し、待機列へ追加します。
- GET /style.css。接続を使用中。JavaScript と画像は待ちます。
- GET /app.js。CSS が完了し、画像はまだ待ちます。
- GET /photo.jpg。最後のファイルです。棒が重ならず、階段状になります。
- HTTP/2 へ。 追加ファイルを並行転送し、CSS データだけを含むトランスポートパケットを落とします。
- ストリーム 3、5、7 を開く。 3 個の HEADERS を 1 本の TCP 接続で送ります。
- DATA。 css、js、img が同じ列を共有します。これが多重化です。
- 損失。 CSS のパケットが欠落。JavaScript と画像は到着済みで、バッファーに残ります。
- TCP HOL。 追加ファイルがすべて止まり、後続バイトは欠落の補完を待ちます。
- HTTP/3 へ。 QUIC ストリームに切り替え、同じ損失を与えます。
- 再び DATA。 3 個のフレームを QUIC ストリーム 4、8、12 で送ります。
- 損失。 CSS は欠けますが、JavaScript と画像は配送できます。
- ストリームごとに進む。 JavaScript と画像は完了、CSS は未完了。CSS の失われたデータを新しいパケットで再送し、他のストリームは待ちません。
- 完了。 待ち行列、TCP の停止、ストリーム単位の配送を比べます。HTTP/3 を有効にするのはオリジンや CDN で、開発者ツールの Protocol 列には
h3と出ます。
単一バージョンのタブでも再生できます。Clean の完了は HTTP/1.1 が rtt = 6、HTTP/2 が 4、HTTP/3 が 3。Lost packet では HTTP/1.1 が 8 で、損失時の JavaScript は未送信です。HTTP/2 は 6 で、欠けた TCP データを再送すると全ストリームの配送が再開します。HTTP/3 は 5 で、再送待ちは CSS だけです。
パイプは現在の RTT のフレーム、ウォーターフォールは全 RTT の履歴です。H1 / H2 / H3 の区切りを使い、棒の重なりと CSS の赤い欠落が他のファイルを止めるかを見てください。
1 パケットの損失が 3 ファイルに及ぶ理由
HTTP/2 より前も、ブラウザーは複数の HTTP/1.1 接続で並行取得していました。HTTP/2 は TLS ハンドシェイクと輻輳ウィンドウを 1 つにまとめます。7 個目のスタイルシートも空き HTTP/1.1 ソケットを待つ必要はありません。ただし、ストリーム数とフロー制御の制限は残ります。
TCP の契約は単一の順序付きバイトストリームです。その上に HTTP の流れを多重化すると、同じ欠落に配送を止められます。QUIC は多重化を保ち、順序を保証する範囲を各ストリームに分けます。
損失なしの RTT は 6、4、3。内訳は次のとおりです。
- HTTP/1.1:接続確立 2 + HTML 1 + 追加ファイル 3。
- HTTP/2:接続確立 2 + HTML 1 + 並行取得 1。
- HTTP/3:接続確立 1 + HTML 1 + 並行取得 1。
損失は欠落と再試行の 2 列を追加します。ただし HTTP/3 の JavaScript と画像は最初の追加ファイル RTT で終わります。これはモデルの集計であり、ベンチマークや実測 RTO ではありません。
境界条件
- 追加ファイルは解析後に現れる。 本例では HTML が示す前に
/style.cssを送りません。実ブラウザーではプリロードスキャナーなどにより解析と取得を重ねられます。 - HTTP/1.1 は未送信の待ち行列。 損失後にパイプが空でも、次の GET がまだ始まらないことがあります。
- HTTP/2 は TCP の欠落待ち。 到着済みでも配送できないフレームがあります。「消失」ではなく「バッファー済み」です。
- HTTP/3 の分離は配送順序の範囲。 接続を閉じれば全ストリームが終了します。1 パケットに CSS と JavaScript の STREAM フレームが入っていれば、両方が欠けたデータを待ちます。共有する輻輳制御、接続のフロー制御、QPACK の依存関係が他の流れを遅らせる場合もあります。
- ID の規則が異なる。 HTTP/2 のクライアント開始は 1、3、5、7、偶数はサーバー用です。QUIC は下位 2 ビットで開始側と方向を表すため、HTTP/3 のクライアント双方向ストリームは 0、4、8、12 になります。
よくある間違い
- HTTP/1.1 の 6 接続を単一接続の多重化と考える。 本例は各接続で 1 件ずつ処理します。HTTP/1.1 パイプラインは応答順序を守り、HTTP/2 は応答フレームを交互に送れます。ただし無制限ではありません。
- HTTP/2 の多重化を HTTP/1.1 のパイプラインと同じにする。 後者は複数リクエストを先に送っても応答順序に縛られ、ブラウザーでは通常使いません。HTTP/2 の制約は TCP の損失によるものです。
- HTTP/3 が常に速いと言う。 この新規接続モデルでは 1 RTT 減りますが、実際にはセッション再開や経路の状態にも依存します。
- QUIC のパケット番号をストリームごとにする。 パケット番号は接続の 3 空間に属します。独立した配送に使うのは STREAM ID とオフセットです。
- HTTPS の説明と混同する。 3 例とも暗号化済みです。HTTP/1.1 と HTTP/2 は TLS、HTTP/3 は QUIC に統合した TLS を使います。鍵の確立は TLS Handshake の教材で扱います。
- ステータスコードだけを覚える。
200は成功応答です。この教材の焦点は、誰が同時に送れ、損失時に誰が待つかです。
モデルの簡略化
実ブラウザーは HTTP/1.1 で同一オリジンに約 6 接続を開き、プリロード、解析、取得を重ねることがあります。HPACK と QPACK はヘッダーを圧縮します。HTTP/2 サーバープッシュは主要ブラウザーの実装から削除されましたが、仕様には残っています。優先度の対応は実装次第です。HTTP/3 の 0-RTT 早期データとセッション再開も省略しています。
失うのは CSS のデータだけを含む 1 トランスポートパケットです。TCP と TLS はまとめたステップで示し、RTT 数は 4 ファイルのモデル内の値です。仕様は RFC 9113(HTTP/2)、RFC 9114(HTTP/3)、RFC 9000 / 9002(QUIC)を参照してください。
続けて学ぶ
同じ手法につながるレッスンや、次の概念への入口です。
鍵マークのあるレッスンの操作には有料アクセスが必要です。解説はすべて無料で読めます。
誤りや気になる点を見つけましたか? フィードバックを送る。