2010-10-30

[perl] AnyEvent::Twitter::Stream で自動再接続処理

Perl関係のツイートをリツイートするうざいボット @AE_PerlIsm を動かして得られた知見についての記事です。

キーワードを含んだツイートは、Twitter Streaming API を AnyEvent からいい感じで取得できる AnyEvent::Twitter::Stream で取得しているのですが、この接続が12~24時間に1回くらい切られるようなので、再接続処理が必要だと思います。

そのコードは以下のような感じでやっています。

注意点は、
接続状態から切れた時は wait なしで再接続する。
接続成功は、30秒おきに届く on_keepalive で判明する場合もある。
リトライウエイトで sleep を使うとイベントループが止まって良くないので、AE::timer を使う。

use AnyEvent::Twitter::Stream;

while (1) {
    my $done_cv = AE::cv;
    my $connected;
    my $streamer = AnyEvent::Twitter::Stream->new(
        method => 'filter',
        track => 'perl',
        # 認証は適当にやっとく

        timeout => 45,
        on_tweet => sub {
            $connected = 1 unless $connected;
            # 新しいツイートを受信したときの処理
        },
        on_keepalive => sub {
            $connected = 1 unless $connected;
        },
        on_error => sub {
            $done_cv->send;
        },
    );
    $done_cv->recv;
    undef $streamer;

    my $wait = $connected ? 0 : 2; #とりあえず2秒まってやる

    my $wait_cv = AE::cv;
    my $wait_t = AE::timer $wait, 0, $wait_cv;
    $wait_cv->recv;
}

2012/1/3 追記
現在の AE_PerlIsm は DotCloud にて、supervisord に依存して、接続が切れたら die で異常終了することで再接続するようにしてみました。再接続回数などの情報を保存して受け継ぐ必要がありますが、この方が簡単に書けると思います。

2010-10-29

[perl] Image::Imlib2 でファイルハンドルに書き込む

mixi で採用されている らしい Image::Imlib2 ですが、出力の方法がファイルに対する書き込みしかなかったので、hidekiy.com の背景画像を動的に生成するのに便利な方法を考えてみました。

IO::File->new_tmpfileで一時ファイル (tmpfile, tempfile) を作って、そのハンドル $fh のファイルディスクリプタに対して save で保存します。$fh のカーソルは先頭のままなので、$fh をそのまま PSGI レスポンスの body とかに使えばよいです。さらに変数に入れたいなら Perl6::Slurp みたいな方法で $fh を読めば OK です。

use Image::Imlib2;
use IO::File;

my $img = Image::Imlib2->new(100, 100);
$img->set_color(128, 128, 128, 256);
$img->fill_rectangle(0, 0, $img->width, $img->height);

$img->image_set_format('png');
my $fh = IO::File->new_tmpfile;
$img->save('/dev/fd/' . $fh->fileno);

$fh;

2010-10-28

[perl] Devel::REPL を使う

CPAN モジュール Devel::REPL をインストールすると Perl の対話環境 (インタラクティブシェル, Read-Eval-Print Loop) が re.pl というコマンドで入ります。公式ドキュメントでは、いまいちカスタマイズの仕方が分からないのですが、起動スクリプトで便利なプラグインを読み込むと良いです。

起動時にデフォルトで読み込まれるスクリプトは、こちらを参照 して、$HOME/.re.pl/repl.rc なので、これを作成します。以下は今使ってるものですが、もっと活用できそうです。便利な機能を発見したらまた更新します。

use strict;
no strict 'vars';
use utf8;
use warnings;
use diagnostics;
use Data::Dumper;
binmode STDOUT, ':utf8';

$_REPL->load_plugin($_) for qw(
    Colors
    Completion
    CompletionDriver::Globals
    CompletionDriver::INC
    CompletionDriver::Keywords
    CompletionDriver::LexEnv
    CompletionDriver::Methods
    DDS
    History
    LexEnv
    MultiLine::PPI
    Packages
    ReadLineHistory
);

2010-10-16

XMLHttpRequest Level 2 時代のクロスドメインリクエスト Proxy

XMLHttpRequest Level 2では、XMLHttpRequest が Same Origin Policy を満たさなくても通信できるようにするための仕組みが追加されています。

その仕組みとは、通信相手のサーバーが HTTP レスポンスヘッダ中でクロスドメインアクセスを許可することを示す、Access-Control-Allow-Origin というヘッダを出力するというものです。このヘッダだけ出力した場合は GET リクエストによるクロスドメインアクセスが対象になります。

この拡張された XMLHttpRequest の機能をもっと活用するために、Google App Engine 上で動く GET で取れる範囲でレスポンスヘッダに Access-Control-Allow-Origin: * (すべて許可) をことごとく追加して返す Proxy を開発してみました。

使い方は http://allow-any-origin.appspot.com/http://www.google.co.jp/ のように後ろにくっつけてリクエストするだけです。

デモ

function xhrdemo() {
    var url = document.getElementById("xhrdemo-url").value;
    var req = window.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest();
    req.open("GET", "http://allow-any-origin.appspot.com/" + url, true);
    req.onload = function (event) {
        alert(req.responseText);
        req = null;
    }
    req.send(null);
}



リンク
allow-any-origin.appspot.com
404 Blog Not Found:Ajax - Goodbye, JSONP. Hello, Access-Control-Allow-Origin
NetAgent Official Blog : IE8+jQueryによるクロスドメイン通信とXDomainRequestラッパーの作成 - この Proxy をご紹介いただきました