Gearmanのサンプルを書いた
perlでキューイングするにはGearmanかTheSchwartzを用いるのが主流。
何となくGearmanのサンプルを書いた。
GitHub - hirafoo/queuing_sample
何はともあれモジュールを入れる。
# cpanm Gearman::Server Gearman::Client Gearman::Worker
プロセス管理
キューイングを使う理由は「重い処理を裏に投げたい」もしくは「それ専用のプロセスが他に居て、そいつに仕事を投げて結果だけもらいたい」というのが主。
gearmanはキューイングはしてくれるが、プロセス管理まではしてくれないので自力でする必要がある。
キューイングするときは大抵重い処理だったり一気に沢山リクエスト来る場合なので、あらかじめforkしておくべし。
ここに素敵な例があるのでパクるといいと思います。僕はパクらせていただきました。
http://d.hatena.ne.jp/tokuhirom/20100201/1264989237
ところで。
gearmanはジョブの登録をするとき関数単位でするが、TheSchwartzはモジュール単位でする。
なのでgearmanのジョブ本体は1個のモジュールにずらっと書き、TheSchwartzの場合は1モジュールに1個書く、というスタイルになる。
別にgearmanでも1モジュールに1個関数を書いても止めやしない。好みの問題。
引数を渡す
同じperlとはいえ別のプロセスに処理を投げるわけなので、配列とかハッシュはそのまま渡せない。
Storable::nfreezeしたものを投げ、受け手側ではStorable::thawするなど。
使う
gearmandを起動。この人はサーバとなる。起動オプションは色々あって、ポートは自動でデフォルトで7003が使われる。
別にrootで動かす必要も無い。普通はdaemonizeして使う。daemontoolsがよろしい。起動オプションにdaemonizeするのもある。
% gearmand --port=7003 --wakeup=10 --debug=1
そしてワーカーを起動。これも普通はdaemonizeする。
% ./gearman_worker_runner.pl
gearmanに仕事をさせるときは、「すぐに仕事をさせ、結果が出るまで待つ」か「キューイングだけしといて、結果を待たない」というのが選べる。
仕事を1個だけさせる
「そんなケース、gearman使うメリット無いんじゃないの」とか言わない。
画像をフンフンするとか、gearmanを使う側とは全然違うタイプのプロセスをさせたいとかの場合。バックエンドで画像専用の仕事をする人がいるなら、そいつに投げた方がいいだろう。とかそんな場合。
スリープして前後の時間を吐き出すだけの簡単なお仕事をさせてみる。
% perl enqueue_gearman.pl -realtime
ここでスリープしている間待たされる。
% cat gearman_foo_result before_after: 1291560455, 1291560461
仕事を1個だけさせる。結果は受け取らない
% perl enqueue_gearman.pl -later
すぐに解放される。
% tail -f gearman_foo_result
実行したシェルはすぐに解放され、あとから結果が書きこまれるのがわかる。
仕事を沢山させる。結果を受け取る
% perl enqueue_gearman.pl -realtime_many
全部終わるまで待たされる。
% tail -f gearman_foo_result
ここでは10回スリープする。秒数はその時の実行回数になる。1回目なら1秒、4回目なら4秒。
普通にそういう処理をさせるとなると、(1+2+3+4+5+6+7+8+9+10)秒かかる。が、裏でforkしているので、全ての処理が同時に走る。ので、一番長い処理に合わせて終わるので、待たされるのは10秒。
do_taskとdispatch_backgroundとadd_task
仕事を1個だけさせるときに、結果を受け取るまで待つならdo_taskで、待たないならdispatch_background。
仕事を沢山させたい場合。結果を受け取らないならdispatch_backgroundを何回も呼べばいい。
結果を受け取る場合はtasksetというものを作る。
Gearman::Clientのpodに書いてる。
# waiting on a set of tasks in parallel my $taskset = $client->new_task_set; $taskset->add_task( "add" => "1+2", { on_complete => sub { ... } }); $taskset->add_task( "divide" => "5/0", { on_fail => sub { print "divide by zero error!\n"; }, }); $taskset->wait;
tasksetにadd_taskでさせたい処理を好きなだけ突っ込んでwaitする。そうすると、全部の処理が終わるまで待つ。
成功時の処理は第3引数のon_completeにサブルーチンリファレンスで渡す。失敗時の処理のon_failも同時に指定出来る。
そのうちTheSchwartzも書く。