HTTP/2 のプライオリティ制御が与える影響の視覚化

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](http://www.softwareishard.com/har/viewer/) を使って視覚化します.

nghttp クライアントは以下のルールにより, リソースのプライオリティを決定します:

1. HTML (最大)
2. CSS (高)
3. Javascripts (JS) (中)
4. 画像 (低)

これはブラウザーが CSS, Javascript を画像よりも先にロードしたいという側面をシミュレートしたものです. 全てのブラウザーがこのプライオリティにしたがっているということではなく, 一般的に必ずこの順にリソースがロードされなければならないということではありません. あくまでプライオリティ制御の実験のために定めているルールです.

nghttp クライアントは `-a` オプションを与えるとダウンロードした HTML から静的リソースリンク (e.g., CSS, Javascript, 画像) を見つけだし, それらもダウンロードするという動作になります. Javascript を解釈しないので動的なコンテンツには対応していません.

Web ページのリソースは, [http2rulez](https://github.com/ipeychev/http2rulez.com) のデータを使いました. サーバーの実装は, nghttp2 に含まれる nghttpx プロキシーサーバーを使い, バックエンドに nginx を配置しています.

それではプライオリティ制御なしの場合についてみてみましょう.

![without-priority](https://nghttp2.org/images/posts/without-pri-blog.png)

CSS は薄いオレンジ色, Javascript は薄い赤色で示されています. 見て分かる通り, CSS, Javascript ファイルは画像とインターリーブされて転送されていて, 転送完了時間が後ろの方になっています. bootstrap.css は 417.3ms, jquery-ui.min.js は468.9ms かかっていて, これらがロード完了した時点ではほとんどの画像のロードも終了しています.

プライオリティ制御を指定するとどのように変わるのでしょうか. 以下がプライオリティ制御を指定した場合の図です:

![with-priority](https://nghttp2.org/images/posts/with-pri-blog.png)

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"  が正しい名称です.