ページの先頭です
2012年12月18日
UNIX系のOSで利用できるWebサーバの性能測定ツールといえば、Apache Benchやhttperfを思い浮かべる人が多いのではないでしょうか。これらの計測ツールは、残念ながら最近の高速なWebサーバを計測するには非力です。この記事では、高速なWebサーバにも負けないweighttpの使い方を紹介します。
weighttpは、Webサーバlighttpdの開発者が実装したWebサーバの性能測定ツールです。以下のような特徴を持ちます。
2.は Apache Benchやhttperfにはない機能です。ここが決定的に異なります。
3.に関して言えば、httperfは古き良きselectシステムコールを使うので性能が出ません。Apache Benchは、APR(Apache Portable Runtime)を利用しているので、APRが選んだプラットフォームに最適なポール・システムコールを利用します。
色々なOSで実行してみると分かるのですが、Apache Benchやhttperfは作りが悪く不安定です。一方、weighttpdはUNIX系OSであれば安定して動きます。なお、Apache Benchの開発は継続されており、httperfの開発は止まっています。weighttpは、一年くらい更新がありません。
weighttpはlibevを利用するので、まずlibevをインストールします。
次にgithubからweighttpのソースを入手して展開し、そのディレクトリに移動します。例えば次のようにやります。
% wget --no-check-certificate -O weighttp.tar.gz https://github.com/lighttpd/weighttp/tarball/master
% tar zxvf weighttp.tar.gz
x lighttpd-weighttp-1bdbe40/
...
% cd lighttpd-weighttp-1bdbe40
ビルドにはPythonで書かれたwafを使います。Pythonがインストールされている環境では、以下のように実行します。
% ./waf configure
% ./waf build
これで、ディレクトリ「build/default」の下にコマンド「weighttp」が作成されます。
Pythonがインストールされてない環境では、以下のようにしてコンパイルできます。
gcc -O2 -DVERSION='"0.3"' src/*.c -o weighttp -lev -lpthread
これで、現在のディレクトリに「weighttp」が作成されます。なお、libevをgccが知らないディレクトリにインストールしている場合には、「-L」オプションでライブラリのパスを指定します。
最後に、作成されたweighttpをパスが通っているディレクトリにコピーすれば、インストールは完了です。
weighttpの引数には、測定対象となるWebサーバのURLを指定します。また、リクエストの総数を指定する「-n」オプションは必須となっています。1つのコネクション上で複数のリクエストを出す「-k」オプションも必ず指定してください。
% weighttp -n 10000 -k http://127.0.0.1:8080/
weighttp - a lightweight and simple webserver benchmarking tool
starting benchmark...
spawning thread #1: 1 concurrent requests, 10000 total requests
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 1 sec, 707 millisec and 611 microsec, 5856 req/s, 1847 kbyte/s
requests: 10000 total, 10000 started, 10000 done, 10000 succeeded, 0 failed, 0 errored
status codes: 10000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 3230000 bytes total, 1720000 bytes http, 1510000 bytes data
上記の例ではコネクションが1つ張られ、10,000個のリクエストが送信されます。スループットの測定結果が、5,856リクエスト毎秒となっているのが分かります。
同時に接続するコネクション数を指定するには「-c」オプションを使います。例えば、1,000個のコネクションを張るには以下のようにします。
% weighttp -n 10000 -c 1000 -k http://127.0.0.1:8080/
この例の場合、それぞれのコネクションで10個のリクエストが送信されることになります。
残念ながらMacではlibevが変な気をまわすため、ポール・システムコールとして、kqueueではなくselectが選ばれるようです。kqueueが選ばれるようにするには、以下のように環境変数を指定します(ここではBシェルの例を示します)。
% export LIBEV_FLAGS=8
以下の表に、LIBEV_FLAGSに対する値の意味をまとめます。
値 | システムコール |
---|---|
1 | select |
2 | poll |
4 | epoll |
8 | kqueue |
実際にどのシステムコールが使われているか知るには、システムコールのトレースをとってみればいいでしょう。Linuxではstrace、BSDではktrace、Macではdtrussを利用すると便利です。
計測側の方が非力であると感じたら、「-t」オプションで作成するネイティブスレッド数を指定しましょう。ネイティブスレッド数を闇雲に大きくしても、コンテキストスイッチが増えて計測性能は向上しません。最大値はコア数だと考えておけばよいでしょう。以下は、ネイティブスレッドの数を4にする例です。
% weighttp -n 10000 -c 1000 -t 4 -k http://127.0.0.1:8080/
「-c」オプションで大きなコネクション数を指定すると、エラーが出て計測できないことがあります。これは、weighttpとWebサーバがリソースの制限にひっかかってしまうからです。
実行しているプログラムに影響を及ぼすリソースの制限は、ソフトリミットと呼ばれます。また、ソフトリミットの上限値をハードリミットと呼びます。多くのコネクションを張って計測する場合は、これらの値を大きくしておく必要があります。
色々なリソースの制限がありますが、ここでは特に重要な以下の2つについて説明します。
listenキューは、保留中のコネクションが繋がれるキューのことです。このキューの長さの制限が小さいと、同時にたくさんのコネクション要求が来た場合、制限を超えた要求を拒否します。ですので、サーバ側でlistenキューのハードリミットとソフトリミットを大きくする必要があります。
listenキューのハードリミットは、sysctlで指定します。以下にsysctlの変数名と初期値を示します。
OS名 | 変数名 | 初期値 |
---|---|---|
Linux | net.core.somaxconn | 128 |
FreeBSD | kern.ipc.somaxconn | 128 |
Mac | kern.ipc.somaxconn | 128 |
このように、どのOSでも歴史的な128という値になっています。これは現在では小さ過ぎます。
以下、Linuxについて説明しますが、他のOSでも変数名が異なるだけです。
コマンドラインでlistenキューのハードリミットを大きくするには、以下のようにします。
% sudo sysctl -w net.core.somaxconn=10000
ブート時にこの値になるよう設定するには、「/etc/sysctl.conf」に以下を書き加えます。
net.core.somaxconn = 10000
listenキューのソフトリミットは listen()システムコールで指定します。利用しているサーバは、教科書通りCのマクロであるSOMAXCONNを指定しているかもしれませんが、SOMAXCONNの値は128なので、小さ過ぎます。もし、SOMAXCONNや小さな値を指定しているのであれば、ソースを変更してサーバをビルドする必要があります。
まず、簡単なBSDやMacから説明します。 ファイル記述子のハードリミットはFreeBSDやMacであれば、sysctlのkern.maxfilesperprocで指定できます。
OS名 | 変数名 | 初期値 |
---|---|---|
FreeBSD | kern.maxfilesperproc | 11095 |
Mac | kern.maxfilesperproc | 10240 |
このように大きな値が使われていますので、ハードリミットの設定は特に必要ありません。もし変更したいなら、上記のsysctlと同じ方法で設定してください。
FreeBSDをサーバにする場合は、何も考える必要はありません。例えばデーモンは/etc/rcから起動されますが、このときファイル記述子のソフトリミットもハードリミットも kern.maxfilesperprocの値になっているようです。
次にweighttp側を考えましょう。ログインした際には、ファイル記述子のソフトリミットは1,024になっています。これは、以下のコマンドで確かめられます。
% ulimit -n
1024
ハードリミットを知るには「-H」も指定します。以下はFreeBSDの例です。
% ulimit -n -H
11095
kern.maxfilesperprocと同じ値になっていることが分かります。Macではunlimitedと表示されるかもしれませんが、実際はkern.maxfilesperprocと同じです。
ファイル記述子のソフトリミットを設定するには、以下のようにします。
% ulimit -n 10000
これで、このシェルから安心してweighttpを実行できるようになりました。毎回ulimitを実行するのが面倒であれば、シェルのRCファイルに書き加えておけばいいでしょう。
Linuxでファイル記述子の制限を大きくするための方法は、測定側とサーバ側で異なります。まず、測定側を説明します。
Linuxでは、ユーザごとにファイル記述子の制限を指定できます。何も指定しなければ、ソフトリミットとハードリミットは共に1,024になっています。ハードリミットを設定するには、「/etc/security/limit.conf」を変更します。以下、ユーザ「kazu」の設定例です。
kazu soft nofile 10000
kazu hard nofile 10000
ここでソフトリミットも指定していますが、実際には意味がありません。ファイル記述子のソフトリミットは、親プロセスの値を受け継ぐからです。通常、ログイン時にはソフトリミットの値は1,024になっています。
% ulimit -n
1024
% ulimit -n -H
10000
BSDやMacと同様に、「ulimit -n」でソフトリミットを設定します。
% ulimit -n 10000
次に、サーバ側を説明します。「/etc/rc」から起動されるデーモンには、「/etc/security/limit.conf」の値は反映されません。これはPAM(Pluggable Authentication Modules)を経由しないからだそうです。
実際の値は、ソフトリミットが1,024、ハードリミットが4,096になっています。よって、デーモンが起動するスクリプトで起動の直前、ulimitを書き加えるしかありません。rootであれば、ハードリミットも大きくできますから、以下のようなコマンドをスクリプトに書き加えることになります。
ulimit -n -H 10000
ulimit -n 10000
ハードリミットを大きくしてから、ソフトリミットを大きくする必要があるので、順番に気を付けてください。
C10K問題(クライアント1万台問題)が叫ばれている割には、実際にサーバがたくさんのコネクションに耐えうるのか検証する方法の解説は、ほとんど見たことがありません。今回紹介したweighttpを使い、リソースの制限を大きくすれば、たくさんのコネクションを張る計測を気軽に実行できるようになります。
執筆者プロフィール
山本 和彦(やまもと かずひこ)
株式会社IIJイノベーションインスティテュート(IIJ-II)技術研究所 主幹研究員
1998年IIJ入社。開発した代表的なオープンソフトにMew、Firemacs、Mighttpdがある。「プログラミングHaskell」の翻訳者。職場ではHaskell、家庭では二人の男の子と格闘する日々を送っている。
関連リンク
ページの終わりです