並列・ 分散処理のためのクローラ
ある程度大規模なクローラを作る場合は,
Perlで高性能なクローラを書く場合の選択肢は2つあります。一つはforkによるマルチプロセスによるもので,
以降では,
AnyEventとCoroの違い
Coroを使うと複雑な非同期処理を,
AnyEventの場合
use AE;
my $i = 0;
my $cv = AE::cv;
# 1秒ごとに,指定した関数を実行せよ
my $watcher = AE::timer 0, 1, sub {
warn $i++;
$cv->send("done") if $i >= 10;
};
# CV = condition variable が利用可能になるまで待機
warn $cv->recv;
Coroの場合
use Coro;
my $main = Coro::current;
async {
my $i = 0;
while (1) {
# async で囲まれた部分だけがsleep によって1 秒止まる
Coro::Timer::sleep 1;
warn $i++;
last if ( $i > 10 );
}
# 次の切り替え時にメインスレッドに切り替わるようにスケジューラに指示
$main->ready;
};
schedule;
Coroで
AnyEventとCoroの使い分け
Coroは黒魔術ですので,
Coro::Timerの内部は次のようになっています。
sub sleep($) {
my $w = AE::timer $_[0], 0, Coro::rouse_cb;
Coro::rouse_wait;
}
内部ではAE::timer を使って指定秒数後にCoro::rouse_
そのためAnyEventとCoroの使い分けは,
Coroを使った典型的なクローラのひな型
Coroを使ってホストごとの接続数制限やウェイトを実装したクローラのサンプルはリスト1のようになります。実際には各処理は複数のクラスに分割したほうがよいですが,
Coroを使うメリットとして,
ホストごとの並列数を制限する
クローラを書くうえで,
たとえば同時接続数を4件に制限したい場合,
my $hosts = {};
sub task {
my $url = shift;
my $host = URI->new($url)->host;
# ホストごとのセマフォを呼び出す,なければ新しく作る
my $semaphore = $hosts->{$host} ||= Coro::Semaphore->new(4);
# 4 件以上呼ばれたら,その時点で別スレッドに切り替わる
my $guard = $semaphore->guard;
# 何らかの処理
...
}
上記のコードでは,
$semaphore->guardを使わずに,
詳しい使い方はperldoc Coro::Semaphore
を参照してください。