Qudoで簡単ジョブキュー処理を実施するB!

前置き

こんにちはmasartzです。今日は僕とid:nekokakさんが共同で作っている

モジュール「Qudo」を紹介させていただきます。

本題

Qudoは既存のジョブキューシステムを使ってみて思ったいくつかの要望を

実現する目的で作成しました。ポイントは拡張性と使いやすさです。

まずは、簡単な例から初めてみましょう。

Advent Calendarということで、クリスマスにプレゼントを願う子供と

それを届けるサンタクロースを再現してみたいと思います。

QudoはデータストアにRDBMSを用いています。

そのため、使用する際にはあらかじめDBを作成する必要があります。

現在MySQL,SQLite,PostgreSQL(多分)に対応しています。

各DB用のスキーマファイルは「http://github.com/nekokak/qudo/tree/master/doc/

にありますので、そちらを参照してDBを作成してください。

以下の内容はMySQLを用いた場合を前提として記載しています。

先に、子供がサンタクロースにプレゼントを注文します。( ClientがJobを投入します )

children.pl

#!/usr/bin/perl 
use strict;
use warnings;

use Qudo;
use MyApp::Worker::Santa;

my $qudo = Qudo->new(
    driver_class => 'Skinny',
    databases => [+{
        dsn      => 'dbi:mysql:qudo',
        username => 'root',
        password => '',
    }],
);

for my $present ( qw/ cake chocolate / ){
        $qudo->enqueue("MyApp::Worker::Santa", { arg => $present });
}

そして、サンタクロースが子供達からのプレゼントを届けます(Jobを処理するWoekerを作ります)

package MyApp::Worker::Santa;

use strict;
use warnings;
use base qw/Qudo::Worker/;

sub work {
    my ($self , $job ) = @_;

    my $present = $job->arg();
    print "Hello , I give you present which you hoped.It is $present.\n";

    $job->completed();
}
1;

最後に、クリスマス当日を迎えてサンタクロースに届けてもらいましょう(workerを実際に起動します)

cometrue.pl

#!/usr/bin/perl 
use strict;
use warnings;

use Qudo;
my $qudo = Qudo->new(
    driver_class => 'Skinny',
    databases => [+{
        dsn      => 'dbi:mysql:qudo',
        username => 'root',
        password => '',
    }],
    manager_abilities => [qw/MyApp::Worker::Santa/],
);

$qudo->work;

cometrue.plを実行した結果、以下のように表示されれば成功です。

(workerはwhileのループで動き続けるので、Ctrl-Cで抜けてください)

Hello , I give you present which you hoped.It is chocolate.
Hello , I give you present which you hoped.It is cake.

と、ここまでが最も簡単な例です。ここから少し応用していきましょう。

応用その1:Enqueueする情報を複雑化させる

まずは、この状態だとサンタは相手を選ばずに適当にプレゼントを

バラまいているので、それぞれの子供に希望の物が届くようにしてあげましょう。

children.plを以下のように変更します。

< for my $present ( qw/ cake chocolate / ){
<     $qudo->enqueue("MyApp::Worker::Santa", { arg => $present });
---
> my @children = (
>     {
>         name    => 'masartz',
>         present => 'cake',
>     },
>     {
>         name    => 'nekokak',
>         present => 'chocolate',
>     },
> );
> 
> $qudo->manager->global_register_hooks(qw/Qudo::Hook::Serialize::JSON/);
> 
> for my $child ( @children ){
>     $qudo->enqueue("MyApp::Worker::Santa2", { arg => $child });

プレゼント名をScalarで渡していたところを、

子供の名前とプレゼントをペアにしたHashRefにしました。

このために、QudoのHook機能を使って、Job をenqueueする時に

引数をシリアライズします。今回はJSONを用いました。

これはオプション扱いですが、Qudoのパッケージに含まれています。

合わせて、Worker側も手を加えます。

<     my $present = $job->arg();
<     print "Hello , I give you present which you hoped.It is $present.\n";
---
>     my $child = $job->arg();
>     print "Hello , $child->{name}. I give you present which you hoped.It is $child->{present}.\n\n";

こちらは、単純に$job->argで取れてくるものをHashRefで

来るように想定して変更しただけです。

さらに、実行するcometrue.plにもHookを使うのを明示化する一文を追加する必要があります。

workメソッドを実行する直前に以下の1行を追加してください。

  manager_abilities => [qw/MyApp::Worker::Santa2 /],
  );
  
+ $qudo->manager->global_register_hooks(qw/Qudo::Hook::Serialize::JSON/);
  $qudo->work;

そして、これで実行してみます。

Hello , nekokak. I give you present which you hoped.It is chocolate.
Hello , masartz. I give you present which you hoped.It is cake.

各Jobで名前とプレゼントの情報を正しく受け取り、実行出来た事がわかります。

子供達がそれぞれ望んだプレゼントを受け取れるようになりました。

ただプレゼントするだけなのも無愛想ですし、せっかくなのでサンタクロースに

お決まりの決め台詞を必ず言ってもらうようにしましょう。

応用その2:Pluginを作ってみる

この実現のために、Plugin機構を使います。

Qudoには様々な場所に独自のPluginを追加出来るようになっています。

先にPluginの中身を作成します。

package MyApp::Worker::Plugin::PreMessage;
use strict;
use warnings;
use base 'Qudo::Plugin';

sub plugin_name{ 'pre_message' }

sub load {
    my $class = shift;

    $class->register(
        sub {
            print "Merry Christmas!! \n";
        }
    );
}
1;

とてもシンプルですが、このメッセージをプレゼントを渡す前に必ず

言ってもらうようにWorkerを修正します。

MyApp::Woker::Santa のprint文の前に以下の1文を追加してください。

  my ($self , $job ) = @_;

+ $job->manager->plugin->{pre_message}->();

  my $child = $job->arg();

最後に忘れずにcometrue.plも修正します。先ほどのHookの時と同じように

wokメソッドより前に以下の1文を追加してください。

  manager_abilities => [qw/MyApp::Worker::Santa2 /],
  );
  
+ $qudo->manager->register_plugins(qw/MyApp::Worker::Plugin::PreMessage/);
  $qudo->manager->global_register_hooks(qw/Qudo::Hook::Serialize::JSON/);
  $qudo->work;

さぁ、これで実行すると以下のようになるハズです。

Merry Christmas!! 
Hello , nekokak. I give you present which you hoped.It is chocolate.
Merry Christmas!! 
Hello , masartz. I give you present which you hoped.It is cake.

成功したでしょうか?

最終形をgistにあげておきました。

children.pl : http://gist.github.com/254925

MyApp::Wokrer::Santa : http://gist.github.com/254928

cometrue.pl : http://gist.github.com/254932

まとめ

Qudoの機能について簡単ではありますが説明させていただきましたが、

いかがでしたでしょうか?

Qudoの開発状況としては、

・CPANにはDevelopper Versionで登録していましたが、つい最近正式版を公開しました

-> http://search.cpan.org/~nekokak/Qudo-0.02

・とはいえ、いくつかTodoも残っているのでgithubで開発は継続中です

-> http://github.com/nekokak/qudo

・質問やご要望等ある方がいらっしゃいましたら、

#qudo@irc.perl.org までお気軽にお越し下さい

という感じです。

今回紹介したような拡張性・使いやすさを念頭においていますので、

使ってみたら感想などいただけると大変参考になります。

あと足りない部分の開発フォローしていただくのも歓迎です。

というわけで今日はここまでです。明日はid:hirose31 さんです。