金利0無利息キャッシング – キャッシングできます

 | 

2009-05-21

Coro::LWPでタイムアウトさせる

19:14 | Coro::LWPでタイムアウトさせる - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - Coro::LWPでタイムアウトさせる - 金利0無利息キャッシング – キャッシングできます

Coro+Preforkで低遅延なサーバーを書こうとして調べてるうちについでに、前にはまってた箇所を理解したのでメモ。

Coroとは何か

  • perlでコルーチンとか継続とかできるようになるモジュールだよ。ググれ!!!!

Coro::LWPとは何か

http://search.cpan.org/dist/Coro/Coro/LWP.pm

  • selectをCoro::Select::selectにグローバルに置き換える
  • LWPで使用するIO::SocketをCoro::Socketに置き換える。
  • 可能であればepoll,kqueueを使うためCPU負荷が減る。
  • Coro::SelectがAnyEvent駆動なので、ファイルハンドルの監視中にAnyEventによる割り込みが出来る。
  • LWPのtimeoutはIO::Socketの'io_socket_timeout'というプロパティを使っていて、Coro::Socketを使うと動かない。

タイムアウト処理を入れる

  • LWP::UserAgentを使う箇所をコルーチン化する。
  • AnyEvent->timer でタイムアウトを監視するイベントを作成する。
  • 時間がかかっているコルーチンがあれば、強制終了する。

という手順でOK。

組み込み関数のselectによるブロッキング(LWPの場合は主に相手先のサーバーからの応答待ち)の場合、waitしている間に他の処理が出来なくなってしまうのに対し、Coro::Select::selectによるファイルハンドルの監視は途中でAnyEventによるイベントを割り込ませることが出来る。

コルーチンのdescriptionにLWP、timeout_atにタイムアウトする時刻を入れて、timeoutしたら$coro->cancelを実行すると強制終了される。LWPなコルーチンが全部無くなったら終了。

指定秒数スリープするCGIにリクエスト、timeout 1秒の場合はキャンセル、timeout 10秒の場合は取得に成功する。

#!/usr/local/bin/perl

use strict;
use warnings;

use Coro;
use Coro::LWP;
use Coro::State;
use LWP::UserAgent;
use Time::HiRes;
use Data::Dumper;

async {
    my $ua = LWP::UserAgent->new;
    $Coro::current->desc("LWP");
    coro_timeout(1);
    my $res = $ua->get("http://localhost/cgi-bin/sleep.cgi?sleep=3");
    print Dumper $res;
};

async {
    my $ua = LWP::UserAgent->new;
    $Coro::current->desc("LWP");
    coro_timeout(10);
    my $res = $ua->get("http://localhost/cgi-bin/sleep.cgi?sleep=3");
    print Dumper $res;
};

sub coro_timeout {
    my $timeout = shift;
    my $coro = $Coro::current;
    $coro->{timeout_at} = Time::HiRes::time() + $timeout;
    $coro->on_destroy(sub {
        my $message = shift;
        warn sprintf "coro:%s cancel because %s", $coro->desc, $message;
    });
}

# timeout watcher
my $w = AnyEvent->timer (after => 0.5, interval => 1, cb => sub {
    my $now = Time::HiRes::time;
    my @lwp_coro = grep { $_->desc eq "LWP" } Coro::State::list;
    warn sprintf "%s lwp coro found.", scalar @lwp_coro;
    for my $coro (@lwp_coro) {
        if ($now > $coro->{timeout_at}) {
            $coro->cancel("timeout");
        }
    }
    if (@lwp_coro == 0) {
        exit;
    }
});

schedule;

__END__

トラックバック - http://subtech.g.hatena.ne.jp/mala/20090521
 |