HTTP2のライブラリ「nghttp2」を紹介するエントリです。
nghttp2のインストールは#1をご覧ください。
ベンチマークツールを使用する
h2loadコマンドでベンチマークを実施できます。
次の例は、100リクエスト、10コネクション、各コネクション最大で5つのDATAストリームを使用する設定でベンチマークを計測してます。-tでスレッド数も指定できます。
h2load -n100 -c10 -m5 https://nghttp2.test:8080/doc/manual/html
starting benchmark...
spawning thread #0: 10 concurrent clients, 100 total requests
Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES128-GCM-SHA256
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done
finished in 34.98ms, 2858 req/s, 150.75KB/s
requests: 100 total, 100 started, 100 done, 100 succeeded, 0 failed, 0 errored
status codes: 0 2xx, 100 3xx, 0 4xx, 0 5xx
traffic: 5400 bytes total, 4260 bytes headers, 0 bytes data
min max mean sd +/- sd
time for request: 173us 12.23ms 3.20ms 3.84ms 75.00%
注意
この計測はインターネットに公開されているサーバーに対して行わないでください。
HPACK tools
ヘッダー圧縮をテストするツールです。
これもGitHubのREADMEに使い方が紹介されていて、その通りに使うだけです。
HTTP2ではリクエストヘッダーのセマンティクスは次のようなものです。
:method: GET
:scheme: https
:path: /
user-agent: nghttp2
これがハフマンコーディングで圧縮されるか、プレーンテキストで送信されます。
nghttp2の圧縮ツールは、JSONかHTTP2セマンティクスのヘッダーのキーバリューを入力して、その値を検証してくれます。
下記のcasesキーで始まるJSONファイルを用意します。
{
"cases":
[
{
"headers": [
{ ":method": "GET" },
{ ":path": "/" }
]
},
{
"headers": [
{ ":method": "POST" },
{ ":path": "/" }
]
}
]
}
標準入力から読み込ませてテストする。
deflatehd < testjson
結果が標準出力に表示されます。
{
"cases":
[
{
"seq": 0,
"input_length": 16,
"output_length": 5,
"percentage_of_original_size": 31.25,
"wire": "3fe11f8284",
"headers": [
{
":method": "GET"
},
{
":path": "/"
}
],
"header_table_size": 4096
}
,
{
"seq": 1,
"input_length": 17,
"output_length": 2,
"percentage_of_original_size": 11.76470588235294,
"wire": "8384",
"headers": [
{
":method": "POST"
},
{
":path": "/"
}
]
}
]
}
Overall: input=33 output=7 ratio=0.21
最初のシーケンスは「:methodGET:path/」で16バイトで、圧縮後は5バイトになり、圧縮率は31.25%で、HTTP2おけるに圧縮後のバイト列は「3fe11f8284」になるという結果。
HTTP2には一度送信したヘッダー値を覚えておいて、次回からは番号だけで指定できる仕組みがあります。これを動的テーブルといいます。
-dオプションを指定すると、動的テーブルを使用したバージョンで動作し、動的テーブルの状態が続けて出力されます。
たとえば次のようなものがseqの後に出力される。
"header_table": {
"entries": [
{
"index": 1,
"name": "user-agent",
"value": "nghttp2",
"referenced": true,
"size": 49
},
{
"index": 2,
"name": ":scheme",
"value": "https",
"referenced": true,
"size": 44
},
次に圧縮データの解凍を行います。
解凍はinflatehdでできます。
圧縮後のヘッダーデータを用意します。
{
"cases":
[
{ "wire": "8285" },
{ "wire": "8583" }
]
}
最初のデータ「8285」は「x82x85」を表現していて2バイトです。
これは「:method GET」と「:path /index.html」を圧縮した結果です。
なぜこれだけの長さを2バイトに圧縮できるのか?
HTTP2では、あらかじめ静的テーブルが定義されていて、よく使用するヘッダーフィルド名、および値とのペアが番号だけで指定できるようになっています。
この表だと「:method GET」が2で「:path /index.html」が5として定義されています。
2 :method GET
5 :path /index.html
静的テーブルや動的テーブルで番号指定でヘッダーフィールドを表現する場合は、MSB(上位4ビット)の最上位に1を立てて、下位7ビットでインデクスを表現します。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | Index (7+) |
+---+---------------------------+
10000002 :method GET
10000005 :path /index.html
↓
0x82 10000002
0x85 10000005
↓
8285
上記の仕組みでかなり高い圧縮率を実現できる場合があります。
次にこの圧縮データを解凍してみます。
inflatehd < jsondata2
{
"cases":
[
{
"seq": 0,
"wire": "8285",
"headers": [
{
":method": "GET"
},
{
":path": "/index.html"
}
]
}
,
{
"seq": 1,
"wire": "8583",
"headers": [
{
":path": "/index.html"
},
{
":method": "POST"
}
]
}
]
}
正しく解凍できました。