PSGI/Plackにおいて、非同期にレスポンスが返せるstreamingという仕様/機能が追加されました。
PSGI/Plack
streaming is now complete
これを使うと、streamingをサポートしたサーバから非同期/nonblockingにhttpやGearmanを利用して外部へ問い合わせを行い、その結果をレスポンスしたりできます。
また、これがPlackで既に実装済みなので、非常に短いコードでサーバの実装ができちゃいます。
すばらしいですね。
すでにmiyagawaさんが、この機能を利用した非同期Web Framework「Tatsumaki」を書かれています。
イベントを扱う部分が隠蔽されているので、これを使うとさらに簡単に実装できます。
すばらしすぐる。
ここでは、簡単に外部へAnyEvent::HTTPを用いて、HTTPリクエストを行うサンプルを書いてみます。
use AnyEvent;
use AnyEvent::HTTP qw//;
$AnyEvent::HTTP::MAX_PER_HOST = 20;
my $url = 'http://localhost/tmp/sleep3.cgi';
my $hanlder = sub {
my $env = shift;
my $cv = AE::cv;
AnyEvent::HTTP::http_get $url, sub {
my $content = shift;
$cv->send(
[ 200, [ "Content-Type" => 'text/plain', ], [$content] ] );
};
return sub {
my $start_response = shift;
$cv->cb(
sub {
$start_response->( shift->recv );
}
);
}
}
PSGIのHandlerでcallbackを返し、その中でhttpアクセスからの通知を待ちます(recv)。 Any::Event::http_get でレスポンスが得られたら、配列形式のレスポンスを構築しcallbackへ通知します。
起動はplackupを利用します。
% plackup --app test.psgi --server AnyEvent port 8080
アクセス先のsleep3.cgiは3秒間のsleepしてからレスポンスを返す通常のCGIです。 なので、このサーバへアクセスすると必ず、レスポンスに3秒かかります。
% time curl http://localhost:8080/
sleep 3
curl http://localhost:8080/ 0.00s user 0.01s system 0% cpu 3.052 total
では、abを利用して、平行してリクエストするとどうなるでしょう。
% time ab -c 10 -n 10 http://localhost:8080/
Concurrency Level: 10
Time taken for tests: 3.328 seconds
Complete requests: 10
Failed requests: 0
Write errors: 0
Total transferred: 720 bytes
HTML transferred: 80 bytes
Requests per second: 3.01 [#/sec] (mean)
Time per request: 3327.714 [ms] (mean)
Time per request: 332.771 [ms] (mean, across all concurrent requests)
Transfer rate: 0.21 [Kbytes/sec] received
ここで重要なのは、3行目のこのテストにあわせてどれくらいの時間がかかっているかです。
3秒×10回の30秒ではなく、ほぼ、3秒しかかかっていません。これは10個のリクエストを非同期に処理、sleep3.cgiへのアクセスし、レスポンスを返していることで実現できています。
これだけコード量も少なくこのような機能をつくれるのはうれしいですねー。