毎秒1000リクエスト を捌く超高速CMS「adiary」
2006/07/19(水)Perl CGIのキャッシュ環境
FastCGI対応は書き間違えだったのですが、どうせならということで対応してみました(対応版の配布はβ4以降になります)。
Perl CGIのキャッシュ動作比較
気づいたらあれこれ対応しすぎた感じがありますが、せっかくなのでどれが一番速いのか試してみしまた(笑) いずれの設定も全くチューニングも何もしていませんので、参考程度にお願いします。
CPU : Pentium3 800MHz (133*6/Coppermine) OS : FreeBSD 6.1-PRERELEASE Web : Apache 2.2 (prefork) ETC : DBキャッシュON TEST : ab -n100 -c4 http://127.0.0.1/xxx/adiary.cgi/user/
テストしたページには適当な日記が数件表示されています。結果は数回実行して頻出値に近い値を取りました。
動作モード | Req/sec | ms/Req |
---|---|---|
cgi | 2.10 | 477.270 |
speedycgi | 15.19 | 65.825 |
mod_speedycgi | 26.41 | 37.863 |
mod_fastcgi | 21.28 | 46.990 |
mod_fcgid | 21.21 | 47.157 |
mod_perl2*1 | 26.69 | 37.473 |
text-file | 196.7 | 5.084 |
mod_fastcgiとmod_fcgidは差がなくて、mod_perl/mod_speedycgiが一歩前に出てるという感じですね。worker動作(スレッドモデル)となると、対応しているのは mod_perl2 vs mod_fcgid だけ。mod_perl2 はいかんせん導入が面倒くさいので、手軽さでは mod_fcgid の方がよいのかもしれません。
本格的にパフォーマンスを求めたり、高負荷時のメモリ消費量の少なさを考えると mod_perl2 on worker MPM に優る選択肢はないのですが個人では必要ないでしょう。*2
ただ、どれも Apache にモジュールを組み込まないとならないので、お手軽に高速化したい場合はSpeedyCGI(ソースコード)をオススメします。パフォーマンスも(個人で使うには)十分ですし、Apacheからは完全にcgiとして見えるので(プロセスが完全に分離するので)、精神的にもよいです。
誰か同じことをMTの管理画面で試さないかなぁ(笑)
各方式の特徴
せっかくなので、簡単に仕組みと特徴を説明。
Perl/cgi
通常のcgi動作。Apacheが、該当のcgiプログラムを fork し実行します。Perl/cgiでは、cgiファイル(使用ライブラリファイル群)のコンパイル時間がとても長く、度々問題となります。*3以下の方式は、どれもこのコンパイル時間を減らすことで大きな高速化を果たしています。
SpeedyCGI(=PersistentPerl)/mod_speedycgi
Apacheからみたら通常のcgiプログラムですが、スクリプト1行目にPerlではなくSpeedyCGIを起動するための
#!/usr/bin/speedy
を記述することでSpeedyCGIが動作します*4。SpeedyCGIは内部的にPerlを呼び出し、実行終了後もそのプロセスを常駐させ(バックエンドと言う)、続いてリクエストが来たときに空いたバックエンドがあればそれに処理を行わせます。
スクリプト側から見た場合、Apacheプロセスと分離されることを除けば*5、全体的な動作はほとんどmod_perlと同じです。
歴史が浅いのか何なのかいまいちマイナーだけど、導入も容易で、mod_perlでは動作しないスクリプトも動作し、Apacheとは別プロセスになるので使うのも気楽です。SpeedyCGIをインストールしてあるレンタルサーバもみかけます。
mod_speedycgiはSpeedyCGIの管理プログラム起動コストを押さえるためApacheにモジュールとして組み込んだものです。それによりかなり高速化されますが、worker MPMには非対応なので注意が必要です。
FastCGI/mod_fcgid
SpeedyCGI的な考え方をより推し進めて、Apache内部にバックエンドプロセスを管理する機能をモジュールとしてインストールしたものです。SpeedyCGIでは、いくらバックエンドがあるとはいえ結局は別プロセスである SpeedyCGI(/usr/bin/speedyそのもの)を fork して実行する必要がありましたが、FastCGIではバックエンドを直接操作するため fork のオーバーヘッドがありません。
mod_fastcgi(FastCGI)とmod_fcgidの差ですが、前者をスレッド動作のApacheに組み込むのはあまりよろしくないとのことです。また巷の噂によると後者の方が速いとかなんとか。なお、FastCGIをスレッド動作のApacheに組み込んだとしても、CGIプログラム(Perlなど)自体は1プロセス=1クライアントとなります。
cgiスクリプトはFastCGI向けに改造する必要があります。
use FCGI; my $count = 0; my $request = FCGI::Request(); while($request->Accept() >= 0) { print("Content-type: text/html\r\n\r\n", ++$count); }
mod_perl
バックエンドどころか、「Perl自体をApacheに抱え込んでしまえばいい」という荒技的な解決策を提示するのが、mod_perl。荒技なだけに、もっともオーバーヘッドが少ない*6方式です。mod_perlの特徴として、Apacheの内部動作をスクリプト側から事細かに制御できますが、移植性(配布性)を考えたソフトではあまり使うことはありません*7。
mod_perlには、Apache 1.x系向けのいわゆるmod_perl(以下mod_perl1と表記)と、Apache 2.0/2.2系向けのmod_perl2があり全く互換性がありません*8。この互換性が度々問題となって、mod_perl1向け書かれたcgiをmod_perl2で動かそうとすると誤動作して厄介な問題を引き起こします。その代表選手とも言えるものがMovable Type*9。
スクリプト側から見た場合、mod_perl1やスレッド動作でないApache2ならば、およそSpeedyCGIと同じような感じです。ただ、Apache2が真価を発揮する worker(スレッド)動作でのmod_perl2は、Perlスクリプト自体もスレッド動作することで高い効率が得られる一方、カレントディレクトリを指定出来ない(chdir()できない)こと*10、あらゆるスクリプトがマルチスレッドでライブラリ空間(メモリ空間)を共有すること*11が大きなネックになります。後からこの対応を行うことは困難であり、これがmod_perl2向けスクリプトがほとんど存在しない原因になっていると思われます。
余談
Apache2(Prefock) + mod_perl2 のときは、次のようにすると自動で chdir されます(いずれか1つ選択)。
PerlResponseHandler ModPerl::RegistryPrefork PerlResponseHandler ModPerl::PerlRunPrefork
2006/04/15(土)mod_perl で chdir
mod_perl2 (on Apache 2.x)で無理矢理MT(Movable Type)を動かそうとしたり、古いスクリプトをmod_perl2で動かそうとする人たちがいまして。そういうページの中で、mod_perl2はカレントディレクトリをスクリプトのdirに変更しないというのが日本ではなぜか迷信のごとく広まっています。……いやある意味では正しいのですけど。例えばこちらのページでは
PerlFixupHandler "sub { \ chdir('/project/sfo/www/www.sfo.jp/lib/mt'); \ return OK; \ }" PerlResponseHandler ModPerl::Registry
とやって明示的に chdir しています(別にやり玉に挙げているわけではなくて、どこのサイトを見てもおよそこんな感じです)。そもそもなぜ、ModPerl::Registry がスクリプトの場所にカレントディレクトリを変更しないかといえば、worker モデルの場合(Apacheがスレッド動作モデル)の場合、chdir しても途中でカレントディレクトリが変わる可能性がある=あまり意味がないからです。だから chdir を明示的に発行するときは、Apache が prefork(従来のプロセスをforkして並列動作する方式)で動いてることを期待しています。
ここからが本題。mod_perl2のマニュアルによるとmod_perl2にはPrefork専用のハンドラーがあり、
PerlResponseHandler ModPerl::RegistryPrefork PerlResponseHandler ModPerl::PerlRunPrefork
と(どちらか一つを)書けば chdir してくれるそうです。まあ英語のマニュアルだから自分もcgi開発でもなければ読まなかったのですが、日本での普及率をGoogle先生に聞いてみるとこんな結果になります(汗)
というわけで、以降普及することを期待して終わり(笑)