Jetty のベンチマークを取ってみた
- Linux version 2.6.18-164.el5xen
- Memory: 532480k
- http://localhost:8080/
Jetty 6
w/o keepalive
concurrency | num | [#/sec] | |||
10 | 10000 | 1352.23 | |||
100 | 10000 | 3793.19 | |||
1000 | 10000 | 3836.32 |
w/ keepalive
concurrency | num | [#/sec] | |||
10 | 10000 | 3351.16 | |||
100 | 10000 | 8065.65 | |||
1000 | 10000 | 6684.50 |
Jetty 7
w/o keepalive
concurrency | num | [#/sec] | |||
10 | 10000 | 910.58 | |||
100 | 10000 | 2116.24 | |||
1000 | 10000 | 2488.36 |
w/ keepalive
concurrency | num | [#/sec] | |||
10 | 10000 | 1333.15 | |||
100 | 10000 | 3350.84 | |||
1000 | 10000 | 4750.80 |
どっちとも負荷を与え続けると
2010-05-17 12:09:34.718:INFO::org.eclipse.jetty.io.nio.SelectorManager$SelectSet@3ff2947d JVM BUG(s) - injecting delay38 times
の警告を出したり
2010-05-17 11:31:19.828:WARN::EXCEPTION java.io.IOException: Too many open files at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:145) at org.eclipse.jetty.server.nio.SelectChannelConnector$1.acceptChannel(SelectChannelConnector.java:75) at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:668) at org.eclipse.jetty.io.nio.SelectorManager.doSelect(SelectorManager.java:182) at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:135) at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:852) at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:437) at java.lang.Thread.run(Thread.java:619)
のExceptionをたまに吐くけど、よくわかんない。
- -
追記:2010/05/17 19:40
http://b.hatena.ne.jp/kazuhooku/20100517#bookmark-21584084
id:kazuhooku「too many open files は ulimit -n == 1024 だからでは? 」
おっしゃるとおりです><
ファイルディスクリプタ増やしたら出なくなりました。
QRコードを表示するapp.psgi
QRコードを表示するだけの簡単なお仕事を大量にさばきたいって言われたので書いてみた。
use Imager::QRCode; use Plack::Request; my $qrcode = Imager::QRCode->new( size => 2, margin => 2, version => 1, level => 'M', casesensitive => 1, lightcolor => Imager::Color->new(255, 255, 255), darkcolor => Imager::Color->new(0, 0, 0), ); my $app = sub { my $req = Plack::Request->new(shift); my $url = $req->param('url') or return [400, ['Content-Type' => 'texxt/plain'], ['url is required']]; my $img = $qrcode->plot($url); $img->write(data => \my $data, type => 'gif') or die $img->errstr; return [200, ['Content-Type' => 'image/gif'], [$data]]; };
これをStarmanで走らせて
starman -a app.psgi
ab -c 10 -n 100してみたら、うちのヘボサーバーでも247.78 [#/sec]くらいでた。
そういえば.cgiはCGIスクリプトってよく言うけど.psgiはPSGIスクリプトって呼べばいいのかな。PSGIアプリケーションでいいのか。
cronologを32bit環境下で動かすときの注意
lighttpdを使ってるとログローテートにcronologをよく使うと風の噂でよく聞きます。
そんなcronologをそのまま32bit環境下で動かすとファイルサイズ2GBを超えて書き込めないですねーというありがたい説法をid:kazuhookuさんにしてもらいました。
要は32bitアプリケーションがopen(2)で2GBを超えるファイルにアクセスするためにはフラグとしてO_LARGEFILEを指定する必要があるのですが、2002-01-24で開発が止まっているcronologはその指定がないのでパッチを当ててやる必要があります。
--- src/cronolog.c.org 2010-02-23 00:49:18.000000000 +0900 +++ src/cronolog.c 2010-02-23 00:50:27.000000000 +0900 @@ -82,6 +82,8 @@ * written to "file" (e.g. /dev/console) or to stderr if "file" is "-". */ +#define _GNU_SOURCE 1 + #include "cronoutils.h" #include "getopt.h" @@ -394,13 +396,13 @@ timestamp(*pnext_period), *pnext_period, *pnext_period - time_now)); - log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE); + log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE, FILE_MODE); #ifndef DONT_CREATE_SUBDIRS if ((log_fd < 0) && (errno == ENOENT)) { create_subdirs(pfilename); - log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE); + log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE, FILE_MODE); } #endif
rpmforgeのパッケージにもパッチはあたっていないのでついでにrpmforgeのSPECファイルを変更するとこんな感じ。
# $Id: cronolog.spec 5040 2007-01-02 20:35:37Z dag $ # Authority: dag Summary: Log file rotator Name: cronolog Version: 1.6.2 Release: 1 License: GPL Group: Applications/File URL: http://cronolog.org/ Source: http://cronolog.org/download/cronolog-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Patch: cronolog-large-file.patch %description cronolog is a simple filter program that reads log file entries from standard input and writes each entry to the output file specified by a filename template and the current date and time. When the expanded filename changes, the current file is closed and a new one opened. cronolog is intended to be used in conjunction with a Web server, such as Apache, to split the access log into daily or monthly logs. %prep %setup %patch -p0 %build %configure %{__make} %{?_smp_mflags} %install %{__rm} -rf %{buildroot} %{__make} install DESTDIR="%{buildroot}" %clean %{__rm} -rf %{buildroot} %files %defattr(-, root, root, 0755) %doc AUTHORS ChangeLog COPYING NEWS README TODO %doc %{_mandir}/man1/cronolog.1m* %doc %{_mandir}/man1/cronosplit.1m* %doc %{_infodir}/cronolog.info* %{_sbindir}/cronolog %{_sbindir}/cronosplit %changelog * Thu Dec 28 2006 Dag Wieers <dag@wieers.com> - 1.6.2-1 - Initial package. (Contributed by Christoper Maser)
とっとと64bitに移行しなさいという天の声が聞こえました。
mac で移行アシスタントを使って困った
会社のmacが交換になったので移行アシスタントとやらで移行してもらったんだけど、色々不具合が…
- /etc/以下のファイルが色々バックアップされてた(hostconfig~(古い Mac から)みたいなファイル名)
- /etc/hosts がバックアップなしに綺麗にデフォルトに戻ってた
- locate が使えなくなってた (/usr/libexec/locate.updatedbで復旧)
- iTerm でxterm-256colors を選択して emacs を上げようとすると emacs: Cannot open terminfo database file と言われて立ち上がらない (TERMINFOを指定してお茶を濁した)
- xcodeは移行されないのでgccがなくなっても驚かない(いまここ)
実名とか匿名とか何とかかんとか
実名(hideo)出したって匿名(hidek)でも呼んでもらえない俺(hideki)がいる。
PlackをProxyサーバーに使う意義
ircで聞いたときはうまく説明できなかった&tokuhiromさん、Yappoさん、kazuhoさんに直接教えてもらったのでまとめとくなり。
Proxyサーバーを作ることになった。
こんな感じのやつ。
で、これの問題として対抗のサーバーの応答速度が遅い場合があってそこにProxyサーバーが引きずられる点がある。つまりクライアントからの毎コネクションが比較的長くなりがちなサーバーをいかに効率よく組むかという課題がある。
最初は勘違いして他のサーバーへの問い合わせの間に他のことをして全体の応答速度を速くする、つまり非同期化によるメリットを模索していたんだけど、1回の応答で他サーバーへの問い合わせがたくさんあるようなクローラーみたいなことをする場合はメリットがあるけど、基本的に1回の応答で他サーバーへの問い合わせは1回だし、コンテンツを持ってくる以外にも処理はあるけど、処理時間の多くはこの1回のコンテンツの取得なのでそもそもこの部分の非同期化の恩恵はあまり受けられない。これはちゃんと理解してなかったので馬鹿みたいな勘違いだった。
対抗のサーバーからの応答が遅い以上、コネクション毎の応答速度を上げることは無理なのは明白なので、同時接続数をなるべく多く確保できる仕組みが要求される。つまり1回のコネクションリソースが少ないもの、もしくはIO待ちが発生している時にリソースの有効利用ができるもの。
となると、preforkではなくマルチスレッドやノンブロッキングIOの上で動く軽量HTTPサーバーの検討に入ったときにPlackが目の前に転がっていた。今回の要件ではコンテンツを持ってくるだけじゃなくてさらに色々処理が発生するのでロジックをPerlで書けることも要件になっていたし、すでにCoroやAnyEventなどの実装を持っているPlackは非常に取っ付きやすかった。今回は単機能なProxyでディスパッチなどは必要ないため、Plackの上にWAFなどは作らず直接実装していくと思う。
ただ、ここで問題なのがユーザーからのリクエストを直接Plackのサーバーが受けられればいいのだが、SSLなどを受ける必要が出てきたときにPlack::Implでは実装しない場合、前にReverse ProxyとしてApacheなどを立てた場合、今度はここでの同時接続が問題となる可能性があるので、ここは考えていかなければいかないと思う。
全体的に理解が浅いしまだまだ勘違いしていることが多そうだけど、問題の局所化はできたので色々ベンチを取りながら色々試していく感じ。