Perl+MySQLで動くジョブキューサーバ『TheSchwartz』を使ってみた

yukiinu2ndです。クリスマスイブですが気にせずPerlしています。
さて、今回は仕事上でどうしてもキューのような仕組みで少しずつ順番に処理をする必要が出てきてしまいました。
一から作るのは面倒&時間がなかったため、探してみたところ『TheSchwartz』が見つかったので使ってみました。

この『TheSchwartz』ですが、PerlだけでなくMySQLを合わせて使う*1ことでジョブキューの仕組みを実装しています。
クライアント(仕事をお願いする側のアプリケーション)はMySQL上のジョブキューにenqueue(RDBMSでいうINSERT)します。
ワーカー(仕事をこなすプロセス)は定期的(デフォルトは5秒間隔)でジョブキューを監視し、ジョブがあれば消化していきます。
一定時間毎に監視するという仕組みのため、リアルタイムで来たものを処理したり、処理性能が求められる用途には向きません。
逆に、メモリ上ではなくMySQLに一旦記録するためワーカーがフリーズしても消えない、信頼性があるという利点もあります。
今回の場合はリアルタイム性は必要なく、ゆっくり仕事してくれればそれでよい、MySQLも動いている、という点でぴったりでした。

インストール

それでは、早速CPANでインストールしてみます。
CPANシェルを起動して

cpan> install TheSchwartz

実行したものの、その前に必要なData::ObjectDriverのインストールでテストに失敗しているようです。
Perl::Criticという文法チェックのところで引っかかっているようなのでforce installしてしまいます。

cpan> force install Data::ObjectDriver

今度こそ、TheSchwartzをインストールします。もう一度……

cpan> install TheSchwartz

としますが、またテストに失敗してしまいました。
これもforce installしてもよかったのですが、http://rt.cpan.org/Public/Bug/Display.html?id=38570を見てテストを直してすっきりインストールします。
CPANがパッケージのダウンロードや展開まで済ませてくれてあるので便乗します。

# cd ~/.cpan/build/TheSchwartz-1.07-ZYS6PA/t/
    <修正作業>
# cd ..
# perl Makefile.PL
# make && make test
# make install

これで無事にインストールできたはずです。

試してみる

インストールも終わったところで実際に動かしてみます。
試すだけであれば、TheSchwartzインストール時のテストが使った残り物のスキーマを使ってしまうのが楽でよいです。
恐らく、『t_sch_ts1』、『t_sch_ts2』ができているはずです。
中には

  • error
  • exitstatus
  • funcmap
  • job
  • note

の5つのテーブルがありました。

それにしても、どういう訳か『TheSchwartz』で使うこういったテーブル群を作る仕組みやスクリプト等がありません。
新しく導入する際にはこのテスト時のテーブル群を『show create table 〜』で調べて同じものを作ればよいと思います。
今回は面倒なのでインストール時のテストが使った物を流用します。


以下にサンプルコードを示します。
まず、ワーカー側。

#!/usr/bin/perl
#
#TheSchwartz Test Script -worker 

package MyWorker;
use strict;
use base qw(TheSchwartz::Worker);#Workerを継承
use Data::Dumper;

#workサブルーチン
#ここに行わせたい処理を書いておく。
sub work {
	#おまじないみたいなもの?
	my $class = shift;
	my $job = shift;
	
	#処理
	print "Workin' hard or hardly workin'? Hyuk!!\n";
	print Dumper $job->arg;
	
	$job->completed();#処理正常完了!
}


package main;
use TheSchwartz;
my $client = TheSchwartz->new(
	#データベース接続情報を配列リファレンスで渡す。
	#接続先は複数記述でき、1つめがダメでも次をトライする、ということをしてくれるらしい。
	#その中はハッシュリファレンスでdsn,user,rootを持つ。
	databases => [{dsn => "dbi:mysql:t_sch_ts1",user => "root",pass => ""}],
	#ログメッセージを出すかどうか。
	#Coderef(サブルーチンへのリファレンス)を渡すとデバッグログ出力時に指定したサブルーチンが呼ばれる
	#それ以外のTrueになるような値であればSTDERRに出力する。
	verbose => 1,
);
#Workerの登録をする。workメソッドを持つパッケージを指定する。(……で間違いなさそう?)
$client->can_do('MyWorker');
#処理開始。定期的にQueueをチェックする。
$client->work();

クライアント側はこちら。

#!/usr/bin/perl
#
#TheSchwartz Test Script -Client-

use strict;
use TheSchwartz;

my $client = TheSchwartz->new(
	#データベース接続情報を配列リファレンスで渡す。
	#接続先は複数記述でき、1つめがダメでも次をトライする、ということをしてくれるらしい。
	#その中はハッシュリファレンスでdsn,user,rootを持つ。
	databases => [{dsn => "dbi:mysql:t_sch_ts1",user => "root",pass => ""}],
	#ログメッセージを出すかどうか。
	#Coderef(サブルーチンへのリファレンス)を渡すとデバッグログ出力時に指定したサブルーチンが呼ばれる
	#それ以外のTrueになるような値であればSTDERRに出力する。
	verbose => 1,
);

#仕事用のデータ
#なんとなく、犬を渡してみる。
my $dat = {dog => "shiba",age => 5};

#WorkerにJobを渡す。
#Worker名をキー、引数を値としたものを渡せばよい、らしい。
$client->insert('MyWorker' => $dat);

まず、ワーカーを実行します。
これでキューを定期的に監視する状態になります。
そのままの状態で放置すると、一定時間(デフォルト5秒)毎に「TheSchwartz::work_once found no jobs」とでます。
表示のままの意味ですが、処理するjobがなかったことを示すメッセージです。
ワーカーが居る状態でクライアントを動かしてみると、Dumperの出力が出て、その前後にはワーカーが働いた旨のメッセージが表示されます。
キューに貯まった仕事をちゃんと消化した、というわけですね。
実際に何度もクライアントを実行すると、その分だけワーカーはメッセージを表示します。
ゆっくり動作させたければ、ワーカー内でsleepしたりすればよいでしょう。

ここで示したのはサンプルですが、色々な使い方ができそうです。

他の候補

他にもジョブキューのようなものを実現する方法はありました。
例えば、Q4MやGearmanです。
しかし、前者はMySQL5.1系が必要だったこと(使いたかったサーバ上で動いていたのは5.0系)、後者は来た瞬間に処理してしまったり、データロストの可能性がある、という点で見送りました。

参考URL

TheSchwartzを使う際に、下記のURLを参考にしましたので、紹介させて頂きます。ありがとうございました。

http://d.hatena.ne.jp/holidayworking/20081123/1227414520
インストール時のテスト失敗を解決する手段が書かれています。

http://d.hatena.ne.jp/tokuhirom/20070501/1177997739
サンプルコードが載っています。

http://labs.gmo.jp/blog/ku/2008/06/theschwartz.html
サンプルコードとワーカーで使用する完了時のメソッドについて日本語で書かれています。

TheSchwartz の worker を安全に停止する - 酒日記 はてな支店
今回触れませんでしたが、ワーカーを安全に終了させる方法について書かれています。

*1:他のRDBMSSQLiteでも使えるらしいですが未確認です。