AnyEventでイベント駆動プログラミング
Web+DB PRESSのvol.56にあったid:lestrratさんの「AnyEventでイベント駆動プログラミング」を読んで、イベント駆動プログラミングに入門してみました。
間違っている点もあるかと思うのでご指摘頂けると幸いです。
イベント駆動プログラミングとは?
- 処理を記述された順番に実行するのではなく、「イベントループ」と呼ばれる処理のタイミングを管理する担当者に、「したい処理」と「処理をしたいタイミング」を伝えることにより、「イベントループ」が処理をお願いしたタイミングで「したい処理」を実行してくれるというものです。
何がうれしいのか。
複数ページのRSSを取得するサンプル
LWP::UserAgentで取得する場合
#!/usr/bin/env perl use strict; use warnings; use utf8; use 5.010; use Encode; use LWP::UserAgent; use XML::Simple; my @uri_list = qw{ http://d.hatena.ne.jp/koba04/rss2 http://tower.jp/feeds/article/ http://ws.audioscrobbler.com/1.0/user/koba04/recenttracks.rss http://atnd.org/events.rss http://rss.rssad.jp/rss/codezine/new/20/index.xml }; my $ua = LWP::UserAgent->new; $ua->timeout(10); for my $uri (@uri_list) { say "HTTP Request => $uri"; my $response = $ua->get($uri); if ($response->is_success) { my $res = XMLin($response->decoded_content); say encode_utf8($res->{channel}->{item}->[0]->{title}); } else { die $response->status_line; } }
HTTP Request => http://d.hatena.ne.jp/koba04/rss2 [勉強会/セミナー]Lightweight Language Tiger HTTP Request => http://tower.jp/feeds/article/ えっ、こんなこともできちゃうの!? HTTP Request => http://ws.audioscrobbler.com/1.0/user/koba04/recenttracks.rss New Order – Regret HTTP Request => http://atnd.org/events.rss 第2回中国GTUG勉強会@岡山 HTTP Request => http://rss.rssad.jp/rss/codezine/new/20/index.xml TIFFライブラリに潜む脆弱性をつぶすパッチ(その2)
AnyEvent::HTTPで取得した場合
- わかりやすい(!?)ようにおかあさんがこどもをおつかいに出すことに例えてみました。。
#!/usr/bin/env perl use strict; use warnings; use utf8; use 5.010; use AnyEvent; use AnyEvent::HTTP; use Encode; use XML::Simple; my @uri_list = qw{ http://d.hatena.ne.jp/koba04/rss2 http://tower.jp/feeds/article/ http://ws.audioscrobbler.com/1.0/user/koba04/recenttracks.rss http://atnd.org/events.rss http://rss.rssad.jp/rss/codezine/new/20/index.xml }; # おかあさん($cv)を作成 my $cv = AnyEvent->condvar; for my $uri (@uri_list) { say "HTTP Request => $uri"; # こどもにおつかいをお願いする $cv->begin; # こどもに指示を出す。 my $guard; $guard = http_get $uri => sub { my ($body, $header) = @_; # 明示的にメモリから解放するタイミングを指定してやる必要がある?? undef $guard; my $res = XMLin(decode_utf8($body)); # おかあさんに結果報告 say encode_utf8($res->{channel}->{item}->[0]->{title}); # こどもが帰ってきたことを確認する $cv->end; }; } # 全員こどもが帰ってくるのを確認するまで待つ(この場合は5人) $cv->recv;
- 結果です。HTTPリクエストがどんどん投げられて、返ってきたものから出力されていることがわかります。
- 最初のLWP::UserAgentの場合だと、こどもが一人帰ってきたら次の一人をおつかいに出すようになっていますが、AnyEvent::HTTPの場合は、こどもをどんどん同時におつかいに出しているので、処理が短く済みます。
HTTP Request => http://d.hatena.ne.jp/koba04/rss2 HTTP Request => http://tower.jp/feeds/article/ HTTP Request => http://ws.audioscrobbler.com/1.0/user/koba04/recenttracks.rss HTTP Request => http://atnd.org/events.rss HTTP Request => http://rss.rssad.jp/rss/codezine/new/20/index.xml TIFFライブラリに潜む脆弱性をつぶすパッチ(その2) PAIP独書会 [勉強会/セミナー]Lightweight Language Tiger えっ、こんなこともできちゃうの!? New Order – Regret
まとめ
- 通信やファイルの読み書きなど、時間がかかるような処理を行う場合は、うまくAnyEventを使うことで短時間で処理することが出来たりと便利そうですね。
- ここでは紹介していませんが、「何秒経ったら実行する」や「ファイルやソケットが書き(読み)込み可能になったら」なども出来るので、うまく使えばとても面白いものが作れそうですね。イマイチわかってない部分もあるのでもっと勉強します。
- なんと、今回参考にさせて頂いた連載の内容はWebで公開されていますのでそちらを参照ください。すごくわかりやすいです。
余談
- 上記のLWPの方のコードは、「perldoc LWP::UserAgent」の結果から取ってきたのですが、そのまま貼付けたときに、下記のようなエラーが出てちょっと焦りました。
Unrecognized character \xE2; marked by <-- HERE after :UserAgent<-- HERE near column 24 at test.pl line 9.
- よく見てみるとソースの「->」の「-(HYPHEN-MINUS, Ux002D)」が「−(MINUS-SIGN, Ux2212)」になっているためエラーになっていました。(Mac OSX)
- 「perldoc -t LWP::UserAgent」とすると、ちゃんと「-」でありそのままコピペして実行してもエラーになりませんでした。 下記のページにあるように「alias perldoc='perldoc -t'」とするのがシンプルな対応なのかなと思いました。