HTTP/2 のプライオリティ制御が与える影響の視覚化について述べます.
この記事は https://nghttp2.org/blog/2014/11/16/visualization-of-http-slash-2-priority/ の日本語訳に, プライオリティに関する若干の説明を加えた内容になっています.
本題に入る前に HTTP におけるプライオリティ制御について話をすることにします. HTTP/1.1 においてはプライオリティという機能は存在していませんでした. ブラウザーは独自の解釈によりリソースのリクエストの順番を複数接続においてスケジューリングすることにより, プライオリティ制御を実現していたのでした. SPDY では原則的に 1 ドメインへは 1 接続となり, その上でリクエストが多重化されています. out of order での処理が可能とはいえ, ファイルサイズの大きな画像データによって CSS や Javascript がブロックされるとページの表示が遅れてしまう可能性があるため, プライオリティ制御機能がプロトコルに組み込まれています. SPDY では非負整数値によりプライオリティを指定します. 0 が最高のプライオリティを意味します.
HTTP/2 では dependency based priority という仕組みが導入されています.
各リクエスト (ストリーム) 間に依存関係を設定します.
依存関係は木構造となりプライオリティが高いストリームほど根に近い部分に位置することになります. この木を dependency tree といいます. プライオリティの設定はクライアントがサーバーに対して一方的にヒントとして通知するということになっていて, サーバーはそれらを考慮してリソースの処理をするということになっています.
HTTP/2 では依存関係に weight を設定できますが, これは同一の依存対象をもつストリーム間でのネットワークやCPU 資源配分の割合を決めるものです. ストリームが他のストリームに依存しない場合, weight を SPDY のプライオリティのように使えるように思えます. しかし weight と依存関係には決定的な違いがあります.
依存関係がある場合, ストリームはそれに依存するすべての下位ストリームをブロックするように動作します. weight はそうではなく, weight の配分にしたがってストリームをインターリーブすることが期待されます. すなわち weight が高いストリームが weight の小さいストリームをブロックすることは期待されていないのです.
やや大雑把に HTTP におけるプライオリティの話をしました. わかっていただきたいことは, 1 接続上に複数のリクエストが多重化される HTTP/2, SPDY にとってプライオリティは必須であり, これがないと HTTP/1.1 よりも悪いユーザーエクスペリエンスにつながる場合があるということです. それでは本題に入りましょう. HTTP/2 においてプライオリティを指定した場合, 指定しない場合での違いを視覚的に比較していきます.
nghttp2 ライブラリはこの文章の執筆時点において HTTP/2 dependency based priority を実装する数少ない実装の一つです. この実験において, nghttp2 に含まれる nghttp クライアントを使います. nghttp クライアントはコマンドライン HTTP/2 クライアントであり, HTTP/2 のストリームトランザクションを HAR フォーマットで出力する機能を備えています. この HAR ファイルを HAR file viewer を使って視覚化します.
nghttp クライアントは以下のルールにより, リソースのプライオリティを決定します:
- HTML (最大)
- CSS (高)
- Javascripts (JS) (中)
- 画像 (低)
これはブラウザーが CSS, Javascript を画像よりも先にロードしたいという側面をシミュレートしたものです. 全てのブラウザーがこのプライオリティにしたがっているということではなく, 一般的に必ずこの順にリソースがロードされなければならないということではありません. あくまでプライオリティ制御の実験のために定めているルールです.
nghttp クライアントは -a
オプションを与えるとダウンロードした HTML から静的リソースリンク (e.g., CSS, Javascript, 画像) を見つけだし, それらもダウンロードするという動作になります. Javascript を解釈しないので動的なコンテンツには対応していません.
Web ページのリソースは, http2rulez のデータを使いました. サーバーの実装は, nghttp2 に含まれる nghttpx プロキシーサーバーを使い, バックエンドに nginx を配置しています.
それではプライオリティ制御なしの場合についてみてみましょう.
CSS は薄いオレンジ色, Javascript は薄い赤色で示されています. 見て分かる通り, CSS, Javascript ファイルは画像とインターリーブされて転送されていて, 転送完了時間が後ろの方になっています. bootstrap.css は 417.3ms, jquery-ui.min.js は468.9ms かかっていて, これらがロード完了した時点ではほとんどの画像のロードも終了しています.
プライオリティ制御を指定するとどのように変わるのでしょうか. 以下がプライオリティ制御を指定した場合の図です:
CSS, Javascript ファイルのロード完了タイミングが早くなっていることがわかると思います. プライオリティ制御により画像の転送が CSS, Javascript よりも遅れるように制御されているのです. bootstrap.css は 62.6ms, jquery-ui.min.js は 159ms でロード完了しています.
このように HTTP/2 ではプライオリティがプロトコルに組み込まれました. プライオリティ制御を使うと, クライアントは早くロードしたいリソースを指定することができます. ページのレイアウトに必要なリソースの優先度を高くすることにより, 素早いページの表示開始など, ユーザーエクスペリエンスの向上が期待できるのです.
この原稿の執筆時点では, Firefox, Chrome ともに dependency based priority を実装していませんが, 両開発関係者からはいずれは実装するという言葉を聞いています. これら主流のブラウザがサポートを開始し, Google などトラフィックの多いサイトのサーバーも実装がすすむことを大いに期待します.
余談ですが, 現在の仕様では "HTTP2.0" ではなく "HTTP/2" もしくは "HTTP2" が正しい名称です.