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

 | 

2009-07-10

Coroでクローラ書くときはCoro::Semaphoreを使うと便利

11:37 | Coroでクローラ書くときはCoro::Semaphoreを使うと便利 - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - Coroでクローラ書くときはCoro::Semaphoreを使うと便利 - 金利0無利息キャッシング – キャッシングできます

Coroでクローラ書くときはCoro::Semaphoreを使うと便利です。

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

Coro::Semaphore->new($num)でリソースの制限数を指定しておきます。$semaphore->downできないと、そこでcoroが停止して別のcoroに処理が移ります。

guardを使うとより便利です。guradメソッドの返値を$guardに保存しておき、$guardが破棄されるタイミングで自動でSemaphoreが++されます。

このままだと動きませんがこんな感じです。

グローバルな並列リクエスト数と、ホスト毎の並列リクエスト数を指定する処理がすっきり書けます。

package MyCrawler;

my $GlobalLock;
my $DomainLock = {};

sub parallel_request_num { 50 }
sub parallel_request_per_host { 2 }

sub global_lock {
    my $self = shift;
    my $num = $self->parallel_request_num;
    my $sem = $GlobalLock ||= Coro::Semaphore->new($num);
    if ($sem->count != $num) {
        $self->log_it("Semaphore for Global: %d", $sem->count);
    }
    return $sem->guard;
}

sub domain_lock {
    my ($self, $host) = @_;
    my $num = $self->parallel_request_per_host;
    my $sem = $DomainLock->{$host} ||= Coro::Semaphore->new($num);
    if ($sem->count != $num) {
        $self->log_it("Semaphore for %s: %d", $host, $sem->count);
    }
    return $sem->guard;
}

sub do_request {
    my ($self, $url) = @_;
    # lock by domain
    my $host = URI->new($url)->host;
    # ロックが取得できなければここでcede
    my $guard = $self->global_lock;
    my $host_guard = $self->domain_lock($host);
    $self->do_fetch();
}
トラックバック - http://subtech.g.hatena.ne.jp/mala/20090710
 |