Gearmanはじめました。

近年のWeb業界ではリクエストがある度にすべてを処理してレスポンスを返すのではなく、一部の処理をジョブキューに投げるなりして負荷軽減を試みるのが定石となりつつあるような、そうでないような、そんな時代に生まれたPerl初心者のために、Gearmanを使ったサンプルコードを書いてみるという記事です。

インストール

たぶんこうするんだと思います。

install Gearman::Server

Gearman::WorkerとGearman::Client,Gearman::Taskはこれで入るやら、はいらないやら、自分の開発環境はずいぶん前にGearman入れていたのですが、ちょっとインストール方法が変わってるような気がします。

ディレクトリ構成

これから紹介する記事はこういう(感じの)ディレクトリ構成です。

/home/hogehoge/eg/client.pl
/home/hogehoge/eg/worker.pl
/home/hogehoge/tmp/gearmand.pid

ストーリー

ある夫婦が食卓で朝食を食べています。この夫婦には息子がいますが、まだベッドの中にいるようです。父親は母親に言いました。「母さん、あの子を起こしてくれないかい?」

父親がclient、母親がworkerだと思って次のコードを試してみてください。

サーバー起動

sudo してもしなくてもいいと思います。

gearmand --pidfile=tmp/gearmand.pid

クライアント

vi eg/client.pl
#!/usr/bin/perl
use strict;
use warnings;
use utf8;

use Gearman::Client;
my $client = Gearman::Client->new;
$client->job_servers('127.0.0.1');

my $result_ref = $client->do_task("wakeup");
print "$$result_ref\n";

なんとなく、このスクリプトを実行すると息子が起きてきそうな気持ちになってきたので実行してみます。

:!perl eg/client.pl

ところがスクリプトがずっと起動したままになってしまいました。なぜでしょう。とりあえずCtrl+cでスクリプトを終了します。

調査

SeeAlso: http://dann.g.hatena.ne.jp/dann/20080727/p1

gearmandをtelnetで覗いてみましょう。gearmandは7003ポートで通常動きます。*1

telnet localhost 7003
status

次のように表示されると思います。

add     1       0       0

意味は次の通り

jobの関数名 キュー中のジョブのトータル数 実行中のjobの数 このjobを処理できるworker数

ジョブをキュー(待ち行列)に配置することには成功していますが、このjobを処理するworkerが存在していません。

しまった。母親がまだ登場していませんでした。

ちなみに、telnetを終了する場合はCtrl+]を押してからquitと入力します。

worker

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Gearman::Worker;
my $worker = Gearman::Worker->new;
$worker->job_servers('127.0.0.1');
$worker->register_function( wakeup => sub { sleep(5); print "good morning!!\n"} );
$worker->work while 1;

実行

:!perl eg/worker.pl

これでお母さんが息子を起こすように伝える準備ができました。workerが起動している状態でclientを起動すると。workerが5秒後に息子を起こします。

clientでは作業が成功した証拠である1が返ります。

お父さんは出かけないといけないのです

今度は父親が出社する時間になったのに、息子がまだベッドの中にいるケースを考えます。父親は玄関で奥さんにこう言いました。「母さん、あの子を起こしておいてくれないか。じゃあ行ってきます。」

先ほどの場合と違って、息子が起きたかどうかを父親が確認する必要がないのでclient.plを次のように書きかえます。

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

use Gearman::Client;
my $client = Gearman::Client->new;
$client->job_servers('127.0.0.1');

#my $result_ref = $client->do_task("wakeup");
#print "$$result_ref\n";

$client->dispatch_background( "wakeup" );

まとめ

最初の例ではClientもWorkerの処理が終わるまでプロセスが動いたままになっているのですが、重たいモジュールを分割してプロセスを軽くしたいなど、プロセスを分割するという事が可能です。

次の例では息子がきちんと起きたかどうかを父親が確認はしていませんが、まあこれは確認しなくても良いのでしょう。心配しなくてもたぶん起きてくれてることでしょう。

このように途中でジョブが破棄されてしまってもさほど問題にならないケースではGearmanを使うのは良い選択だと思います。

追記

こういう便利なものもあります。

http://d.hatena.ne.jp/walf443/20100604/1275610369

*1:そうでもない人もいるとはおもいますがここではこの前提で解説します。