curl でサッと HTTP ベンチマーク

## 何故 curl を使うか

HTTP のベンチマークといえば ab, JMeter, wrk など専用のツールがありますが、何故あえて単純な HTTP クライアントである curl を使うのか。
それは curl がある種の共通言語になっているからです。

* [WebAPIリクエスト仕様書としてcurlコマンドのご提案](http://qiita.com/Hiraku/items/dfda2f8a5353b0742271)
* [cURL as DSLとは何だったのか。あるいは細かすぎて伝わらないcURL as DSL。](http://blog.shibu.jp/article/115602749.html)

さらに、いくつかのツールには HTTP リクエストを curl コマンドとしてコピーする機能が実装されています。
例えば Google Chrome では Developer Tools で Network タブ上から任意のリクエストを curl コマンドとしてコピーできます。

![image](https://qiita-image-store.s3.amazonaws.com/0/208/d805099d-56b5-980f-7084-08370924ffaa.png)

また、mitmproxy にもそのような機能があるようです。 (まだ自分では試してませんが)

* [mitmproxy 0.16 にリクエスト内容をcurlコマンドやPythonコードに変換できる便利なやつがついていた](http://qiita.com/laiso/items/2dc52a71e8a2748fcb99)

つまり、PC ブラウザ上でのリクエストも、スマートフォンアプリ等でのリクエストも、curl コマンドとして簡単に取得できます。
スマートフォンアプリで具体的にどうするかは以下の記事を参照してください。

* [モバイルアプリ開発者のための mitmproxy 入門](http://qiita.com/hkurokawa/items/9034274cc1b9e1405c68)

さらに、こういった方法で curl コマンドをコピーできると嬉しいのは、HTTP ヘッダ等を含めほぼ完全な HTTP リクエストを取得できるということです。
例えば Authorization ヘッダやセッション ID のクッキーが必要な場合に、それらの値をいちいち調べたり、さらにツールごとにどうやって渡せばいいか、といったことを調べる必要がなくなります。

## curl でレスポンスタイムを取得する

まずは curl コマンドを用意します。
今回は自分のブログのトップページのものを Google Chrome から取得してみました。
(Cookie は削っています)

```
$ curl 'http://blog.yuyat.jp/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed
```

このままだとレスポンスボディが出力されるだけです。
このコマンドの末尾に以下を加えます。

```
-s -o /dev/null -w  "%{time_starttransfer}\n"
```

`-s` はリクエスト中の進捗表示を抑制するため、`-o` はレスポンスボディを `/dev/null` に捨てるため。
そして大事なのは `-w` です。
これは様々な値を指定したフォーマットで出力するためのもので、ここではレスポンスタイムとして `time_starttransfer` を指定してみました。
(レスポンスタイムとしてこれが適切なのか、は正直あんまり自信ないのでツッコミ待ちです)
このオプションで取得できるものは様々なので、例えばレスポンスタイムだけでなくレスポンスの全てが帰るまでの時間を計測したい、といった場合などはより適切な値を選択してください。

* [-w, --write-out <format>](https://curl.haxx.se/docs/manpage.html#-w)

これを実行すると以下のように出力されます。

```
$ curl 'http://blog.yuyat.jp/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed -o /dev/null -w  "%{time_starttransfer}\n" -s
0.582
```

レスポンスタイムが 0.582 秒だとわかりました。

## 応用編: レスポンスタイムのパーセンタイル値を取得する

1 回だけのリクエストでは適切なベンチマークとは言えません。
ここでは 100 回リクエストを行ってそのパーセンタイル値を取得してみましょう。
(もっと多い方がいいかもしれないけど、あくまでこの記事はサッとやるだけなのでこの辺で)

拙作の [`ntimes` というコマンド](http://qiita.com/yuya_takeyama/items/673ad8861ca0bda83a7f) を使えば簡単にできます。

先ほどのコマンドの先頭に `ntimes 100 --` を追加するだけです。

```
$ ntimes 100 -- curl 'http://blog.yuyat.jp/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed -o /dev/null -w  "%{time_starttransfer}\n" -s
0.626
0.821
0.710
0.572
0.563
0.556
(以下略)
```

また、`ntimes 100 -p 4 --` というように `-p` オプションを使えば並列数を指定することもできます。

この通り、1 回では 0.582 秒でも、繰り返し実行してみると幅があることがわかります。
というわけでこれのパーセンタイル値を取得してみましょう。
これまた拙作の [`percentile` というコマンド](http://qiita.com/yuya_takeyama/items/4285d4b75f02c67d90fc)を使います。
さっきのコマンドをパイプでこの `percentile` に繋げるだけです。

```
$ ntimes 100 -- curl 'http://blog.yuyat.jp/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed -o /dev/null -w  "%{time_starttransfer}\n" -s | percentile
50%:    0.575
66%:    0.655
75%:    0.715
80%:    0.734
90%:    0.929
95%:    1.167
98%:    1.472
99%:    1.937
100%:   2.613
```