こんにちは!ネットワークの世界へようこそ。インフラ・プロトコルスペシャリストの視点から、日頃私たちが何気なく使っているWebの裏側のドラマをお届けします。
皆さんは普段、ブラウザでWebサイトを見るとき、どれくらいの画像やデータが一気に読み込まれているか意識したことはありますか?現代のWebを支える「HTTP/2」というプロトコルには、実は「相手が受け止めきれなくなるのを防ぐ」ための、とっても優しくて賢い交通整理の仕組みが隠されています。
今回は、その心臓部である「HTTP/2フロー制御(Flow Control)」と、そこで主役を演じる「`WINDOW_UPDATE`フレーム」について、身近な例えを交えながら一緒に一歩ずつ紐解いていきましょう!
—
1. HTTP/2の「マルチプレクシング」という便利な悩み
HTTP/2の最大の特徴といえば、1本のコネクション(通信のパイプ)上で、複数のデータを同時にパラパラと送り合える「マルチプレクシング(多重化)」ですよね。
昔のHTTP/1.1では、1つずつ順番に料理を運ぶ「わんこそば」方式でした。だから、大きな画像を読み込んでいる間は、他のテキストデータが後ろで待たされてしまっていたんです。それをHTTP/2は、大きなパイプの中にいくつもの「レーン(ストリーム)」を作り、同時に荷物を送り出せるようにしました。
ここで、ちょっと想像してみてください。
あなたは巨大な倉庫(サーバー)から、小さな荷受け箱(クライアントのブラウザ)へ向けて、何十個もの荷物を一斉にすごいスピードで送り出しています。
もし、荷受け箱のキャパシティ(バッファサイズ)を無視して、サーバーが無限に荷物を投げ込み続けたらどうなるでしょうか?
そう、荷受け箱があふれかえり、床に荷物が散乱(パケットロスやバッファオーバーフロー)してしまいますよね。「いやいや、こっちはそんなに一気に受け取れないよ!」と、受け手が悲鳴を上げる事態になってしまいます。
この「送り手と受け手のペース配分」を完璧にコントロールするために用意された仕組みこそが、今回学ぶ「HTTP/2のフロー制御」なのです。
—
2. 郵便配達で例える「WINDOW_UPDATE」の仕組み
このフロー制御の動きを、身近な「郵便配達」に例えてみましょう。
- サーバー(送り手): あなたに荷物をどんどん送ってくる通販会社
- クライアント(受け手): 荷物を受け取るあなた(自宅の玄関=バッファ)
HTTP/2のフロー制御では、通信が始まったときに、受け手であるあなたがサーバーに対してこう宣言します。
「私の玄関には、とりあえず『64KB分』までの荷物を置くスペースがあります!」
この「現在受け取れる残り容量」のことを、HTTP/2の世界では「ウィンドウサイズ(Window Size)」と呼びます。
サーバーはこの宣言を聞いて、大喜びで荷物を送り始めます。
10KBの荷物を送りました。残りスペースは54KBです。
さらに20KBの荷物を送りました。残りスペースは34KBです。
そして、サーバーがどんどん荷物を送り、あなたの玄関の残りスペースが危うくなってきたとします。このままでは玄関がダンボールまみれになってしまいますよね。
そこであなたがどうするか?
空いたダンボールを片付けて、中身を部屋(アプリケーション)に移動させたら、すかさずサーバーに向けて「おーい、整理が終わったから、あと『30KB分』の荷物を追加で送ってもいいよ!」というハガキを出します。
この「追加のスペースができたよ、もっと送っていいよ」と伝えるハガキこそが、今回主役の『WINDOW_UPDATEフレーム』なのです!
—
3. フロー制御の2つのレイヤー(ここがポイント!)
HTTP/2のすごいところは、この交通整理を「2つの階層」で行っている点です。几帳面でしょ?
1. ストリーム単位のフロー制御(Stream-level)
- 個別のファイル(例えば、画像Aと画像B)ごとの交通整理です。「画像Aはちょっとゆっくり送って、画像Bは急いで送って」といった細かい調整ができます。
2. コネクション単位のフロー制御(Connection-level)
- すべてのストリームをまとめた「大元のパイプ全体」の交通整理です。全体の総量として、これ以上メモリを食い潰さないための全体リミッターとして働きます。
サーバーは、これら2つの「ウィンドウサイズ」の残量を両方とも確認しながら、配る量を調整しているんです。
—
4. 実務でどう見える?パケットのやり取りをのぞいてみよう
「理屈は分かったけれど、実際のネットワーク上ではどうなっているの?」
エンジニアなら気になるところですよね。開発者ツールやパケットキャプチャ(Wiresharkなど)を覗くと、このやり取りは次のようなバイナリデータのフレームとして観測されます。
[Client] [Server]
| |
|—- HEADERS (画像をちょうだい) ————>|
| |
|<--- DATA (データ本体: 16KB) --------------| (ウィンドウが16KB消費される)
|<--- DATA (データ本体: 16KB) --------------| (ウィンドウがさらに16KB消費される)
| |
|---- WINDOW_UPDATE (追加で32KBOKよ!) ----->| ★ここで受け手が枠を回復させる!
| |
|<--- DATA (次のデータ本体) ----------------|
実際に、HTTP/2を実装・設定する際に見かけるパラメータの例を少しだけ覗いてみましょう。例えば、NginxやGo言語のHTTP/2サーバー設定などで目にするイメージです。
// 【参考コードイメージ:Go言語のHTTP/2サーバー設定の断片】
// ネットワークの現場では、初期のウィンドウサイズをチューニングすることもあります
s := &http.Server{
Addr: ":443",
}
// HTTP/2の細かい設定を行うための構造体
// InitialWindowSize は、ストリームごとの初期ウィンドウサイズを指定します(デフォルトは65535バイト = 約64KB)
http2.ConfigureServer(s, &http2.Server{
MaxUploadBufferPerConnection: 1048576, // コネクション全体のバッファ上限 (1MB)
MaxUploadBufferPerStream: 65536, // ストリームごとの初期ウィンドウ (64KB)
})
もし、クライアント側の処理が追いつかずにアプリのメモリがカツカツになると、この `WINDOW_UPDATE` フレームの送信がピタッと止まります。するとサーバー側も「おっと、相手の玄関がいっぱいだな」と察知し、データの送信を自然と待機(バックプレッシャー)してくれます。
このおかげで、低スペックなスマートフォンから最新のハイスペックPCまで、どんな環境でも破綻することなくスムーズにWebページが表示されるわけですね。
---
5. まとめ
いかがでしたでしょうか?
一見難しそうに見える「HTTP/2のフロー制御」と「`WINDOW_UPDATE`フレーム」も、要するに「送り手と受け手が息を合わせて、荷物の溢れ出しを防ぐためのスマートな連絡網」です。
- HTTP/2のマルチプレクシングは便利だけど、受け手のキャパシティ管理が不可欠。
- 「あとこれだけ受け取れるよ」という残量をウィンドウサイズで管理している。
- スペースが空いたら、`WINDOW_UPDATE`フレームで「もっと送ってOK!」と伝える。
- この仕組みのおかげで、メモリあふれを防ぎながら安全に高速通信ができる。
ネットワークやプロトコルの世界は、こうした「現実世界のコミュニケーションのルール」によく似た、泥臭くて美しい工夫で満ちあふれています。
日々のインフラ運用やトラブルシューティングで「なぜここでパケットが止まっているんだ?」と思ったときは、ぜひ今回の「玄関のダンボール箱」の例えを思い出してみてくださいね。
それでは、また次回の技術探訪でお会いしましょう!
コメント