Isucon用Webサーバーrecaro
11/3に開催された #isucon2 に、隣の席の @pandax381 と一緒に、チーム双龍として参加してきました。 結果は惨敗だったのですが、そのレポートを書く前に、 #isucon2 で使う予定だった秘密兵器 recaro について紹介します。
recaro とは
recaro はカーネル空間で動く httpd + memcached サーバーです。
httpd サーバーは @pandax381 が作成した tkhttpd で、 memcached は kmemcached というプロジェクトが 未完成のまま放置されていたのを見つけて、私がデバッグ&高性能化したものです。 (KLab/kmemcached)
通常のnginx+memcachedだと
ネットワーク <- TCP/IP ]-> nginx <-[ TCP/IP ]-> Memcached ([] はシステムコール)という構成になるところが、 recaro だとWebサーバーがMemcachedのストレージから 直接値を読み出せるので、
ネットワーク <- TCP/IP -> Webサーバー / memcachedストレージという構成になり、 TCP/IP プロトコルスタックをまるまる1回スキップできる上に システムコールも0回にすることができます。
isucon1 では最終的にフロントのWebサーバーで直接静的ファイルやキャッシュを返し、 アプリ側には極力リクエストを飛ばさない構成が強かったのをふまえ、nginxやApacheに 圧勝できる最凶のWebサーバーとして用意しました。
isucon2 では isucon1 ほど簡単にキャッシュ配信競争にならないと予測はしていたのですが、 自分たちで作ったサーバーなら隅々まで理解しているので、当日自分たちで専用の機能を カスタムで作成できると踏んでいました。
インストール
Debian系なら apt-get install build-essentials linux-headers
を、
RedHat系なら yum install gcc make kernel-headers-`uname -r`
で必要なファイルをインストールしておきます。
git clone git://github.com/KLab/recaro.git cd recaro make sudo insmode recaro.ko
0.0.0.0:11211 で memcached が、 0.0.0.0:80 で httpd が動き始めます。
$ python mcset.py $ curl http://127.0.0.1/foo fizzbuzz
となれば成功です。
静的コンテンツ配信
memcached に、 key として path を、 value として mimetype<CR><LF>content
という形式のデータを
格納すると、それを配信します。
例えば、 mcset.py で /foo
というキーに対して
text/plain<CR><LF>fizzbuzz
という値を設定していますが、この場合 http://127.0.0.1/foo
にアクセスすると fizzbuzz が返ってきます。
SSI機能
mimetype に text/html
を設定した場合、SSI機能が有効になります。
コンテンツ中に <!--# include hoge -->
と書くと、 Memcached から hoge
というキーの
値を取得して置換します。
mcset.py の /bar
を参考にしてください。
リバースプロクシ
リクエストメソッドがGETじゃなかった場合や、GETのURLがMemcachedに存在しなかった場合、 http://127.0.0.1:8080/ にリバースプロクシします。
リバースプロクシ先の設定やロードバランシングは当日の状況を見て実装する予定でした。
ベンチマーク
Amazon EC2 の c1.medium インスタンスに nginx と recaro を設置し、同じ zone にある c1.xlarge インスタンスから ab をかけてみます。
nginx は 1.3.8 に lua-nginx-module を加えてビルドしたもので、設定は以下のとおりです。
worker_processes 2; # c1.medium は2コア. events { worker_connections 1024; } http { server { listen 8000; server_name localhost; location / { content_by_lua "ngx.say('fizzbuzz')"; } } }
ab -k -c100 -n100000
を3回実行た結果が次のようになりました。
nginx 26889.80 25937.49 25834.43 recaro 44733.20 44230.13 44171.76 recaro(SSI) 42610.56 43106.59 42471.77
(補足: XenのDomU ではなく物理マシンであれば、5年前のデスクトップPCでも10万req/secを超えます)
nginx は外部の memcached にアクセスしたらこれよりずっとスコアが 落ちるはずなので、 isucon1 の様に一部動的な要素を含むHTMLの配信競争で、帯域が問題に ならないのであれば圧勝できるはずでした。 (続く)