もうずいぶん前からCoroを使いたくて使いたくてしょうがなかったのだが、やっときたよ。Flickr APIを叩いて、非同期I/OしながらCoroで写真の情報をダウンロードするよ!
まず某sukebeさん作のWebService::Simpleを使ってみるよ(本当に使ったコードは違うフレームワーク用にいろいろついてたので、あまり利便性とか考えずに説明用のコードだけ書きだします)
package MyFlickrModel;
use strict;
use WebService::Simple;
my $webservice = WebService::Simple->new( \%必要な引数 );
# 写真のIDを渡すとinfo, context, sizes, permsを全部ひっぱってくる
sub find_photo
{
my $class = shift;
my $photo_id = shift;
my %photo;
foreach my $type qw(info context sizes perms) {
my $method = "find_photo_$type";
$photo{$type} = $class->$method($photo_id);
}
return \%photo;
}
# infoをひっぱってくる
sub find_photo_info
{
my ($class, $id) = @_;
my $response = $webservice->get( {
method => "flickr.photos.getInfo",
photo_id => $id
} );
# エラーチェックとかはしょってます
return $response->parse_xml;
}
# あと同じようにcontext, sizes, perms用の関数を書く
# で、よびだす
MyFlickrModel->find_photo( $photo_id );
でもこれだと写真1個につき都合4回、順番にFlickr APIを叩かなくちゃいけないね。そんなに待ちたくないんだな。非同期にしたいじゃん?で、ここでCoroですよ
Coroはcoroutineの実装をPerl5上でするわけですな。で、ちょっとハックだけれども、LWP::UserAgent系のモジュールの中身も適当にいじってくれるモジュールも用意してくれている。なのでそこらへんをまず追加:
use Coro;
use Coro::Event;
use Coro::LWP;
use WebService::Simple; # 必ずCoro::LWPのあとに
で、先ほどのfind_photoをちょっと書き換える。ほんとにちょっとですよ?
sub find_photo
{
my $class = shift;
my $photo_id = shift;
my %photo;
my @coros; # Coroを取っておく
foreach my $type qw(info context sizes perms) {
push @coros, async { # ここを別Coroに切り分ける
my $method = "find_photo_$type";
$photo{$type} = $class->$method($photo_id);
};
}
$_->join for @coros; # Coroが終了するのを待つ
return \%photo;
}
以上! これだけで、10個の写真を検索→その後find_photo()で詳細を取得、というのが単純に17秒弱から5秒強に減った。Coroすげぇ
ちなみに同じことをしつつ、Cache::Memcached::libmemcachedでキャッシュすると0.2秒で終わるよ!
コメント