せっかくのイベントドリブンフレームワークな POE ですから Comet でチャットをやってみようかと。
http://d.hatena.ne.jp/dayflower/20061116/1163663677
こちらの POE::Component::HTTP による Comet チャットサーバですが、どのぐらいの接続まで耐えられるのか? 実験。
クライアントとして、HTTP::Async を使って同時接続を張りまくるスクリプトを用意。
#!/usr/bin/perl use HTTP::Async; use HTTP::Request; use Time::HiRes qw/time/; use strict; my $slots = shift || 20; my $async = HTTP::Async->new( slots => $slots ); my $req = HTTP::Request->new( GET => 'http://localhost:8888/view' ); while ( 1 ) { my $start = time; $async->add( $req ) for ( 1 .. $slots ); my $conn_t = time - $start; printf "c,%d,%f,%f\n", $slots, $conn_t, $conn_t / $slots; $start = undef; my $count = 0; while ( my $res = $async->wait_for_next_response ) { $start = time unless $count++; } my $res_t = time - $start; printf "r,%d,%f,%f\n", $slots, $res_t, $res_t / $slots; sleep 1; }
long poll する url に接続し、レスポンスが返ってきたら 1秒 sleep して再接続する、というもの。
まずはメモリ使用量 (VSZ)。
接続数 | VSZ |
---|---|
0 | 13412 |
10 | 14008 |
20 | 14100 |
50 | 14232 |
100 | 14760 |
200 | 15816 |
300 | 17000 |
500 | 18980 |
1000 | 24260 |
2000 | 24656 |
次に接続数毎の、コネクションを張り終わるまでの時間 (c) と、long poll から返ってきたレスポンスの最初の一つめを受け取ってから最後のを受け取るまでの時間 (r)。単位は秒。
type | conn | time | per_conn ------+------+-------------+------------- c | 10 | 0.00859111 | 0.000859 r | 10 | 0.000830875 | 8.3125e-05 c | 50 | 0.0460453 | 0.000921 r | 50 | 0.0282752 | 0.0005655 c | 100 | 0.0924309 | 0.000924364 r | 100 | 0.073008 | 0.0007301 c | 200 | 0.19919 | 0.0009959 r | 200 | 0.234428 | 0.00117211 c | 300 | 3.30754 | 0.0110251 r | 300 | 0.528443 | 0.0017615 ========================================= c | 500 | 0.599251 | 0.00119875 r | 500 | 1.17672 | 0.00235343 c | 1000 | 1.35176 | 0.00135175 r | 1000 | 4.64576 | 0.00464571 c | 2000 | 2.62022 | 0.00131017 r | 2000 | 9.47508 | 0.0047376
接続数を増やしながら計測してみると、どうも conn=230 あたりを境に、急に (c) が悪化する現象が発生。
最初はクライアント側の問題か? と思ったのだが、そうではなく。
fedora core 5 のデフォルト設定で、SOMAXCONN が 128 になっている。
# cat /proc/sys/net/core/somaxconn 128 # perl -MSocket -e 'print SOMAXCONN' 128
で、POE::Wheel::SocketFactory でも SOMAXCONN の値を、queue の最大値に使用している。
my $listen_queue = $params{ListenQueue} || SOMAXCONN;
この値を大きくしたら、接続に時間が掛かる現象が解消した。具体的には
# echo 4096 > /proc/sys/net/core/somaxconn
して、更にチャットサーバのスクリプトの方で
sub Socket::SOMAXCONN { 4096 }
とした。本来は POE::Wheel::SocketFactory のコンストラクタで ListenQueue 引数を渡すべきなのだが、これが PoCo::Server::TCP 内にあるため手を入れられず。強引だが Socket::SOMAXCONN を再定義。
ということで、(c) >= 500 の結果は SOMAXCONN をいじった後の結果。
結果については、チャットとして快適なレスポンス、と思えるのは (c)==500 ぐらいまで。すべての (500クライアントに対する) レスポンス送信が 1秒ちょっとで終わる。
1000 になると、発言してから反映されるまでに最悪 5秒掛かるので、ちょっと重い感じ。(もちろん、一番最初に受け取るクライアントはほぼ 0秒だし、平均だと 2.5 秒)
まあ、500〜1000接続 (ほぼ繋ぎっぱなし) を 1台でまかなえれば、結構いいんじゃないでしょうか。
- サーバ側で複雑な処理をするとメモリ使用量はもっと増えるはず (クライアント毎のリソースは大して変わらないから大丈夫か?)
- 特に送信時に時間が掛かることをすると、n クライアントに一斉に送信するので n 倍で効く
このへんには注意したほうがいいかも。
1台で捌けなければ → Spread Toolkit で Comet チャットサーバをクラスタリング - 酒日記 はてな支店 で。