Kentaro Kuribayashi's blog

Software Engineering, Management, Books, and Daily Journal.

Gearman::Worker#register_functionで登録するfunctionを、ちょっといい感じにしてみた

今日は、Gearman workerのmanager(workerプロセスいっぱい作ったり、管理したりするようなひと)を作っていました。ところで、Gearman::Workerを使っている場合、Gearmanのworkerは、こんな風に登録します。

use Gearman::Worker;
my $worker = Gearman::Worker->new;
   $worker->job_servers('127.0.0.1');
   $worker->register_function('funtion name' => sub {
       my $job = shift;
       # ... do something ...
   });

しかし、このように普通に登録してしまうと、Gearman::Jobのインスタンスひとつがわたってくる関数を登録するしかなくて、階層的にfunctionを構成することが難しい。僕としては、普通のクラス階層があって、それらクラスの、たとえばworkメソッドがworker functionの実装になる、というようなことがしたいわけです。

つまり、以下のようなMyApp::Worker::**という、MyApp::Workerを継承した、それぞれのworkメソッドでworker functionを実装したクラスがあったとして、

  • MyApp::Worker
    • MyApp::Worker::Foo
    • MyApp::Worker::Bar
    • MyApp::Worker::Baz

それらのworkメソッドをworker functionとしてGearman::Workerでregister_functionするという感じ。

そこで、今回書いたworker managerでは、以下のようにしてひとつdispatcherを挟むようにしました。

worker managerのコード中、functionを登録するところ抜粋

# $self->worker_functionsは、上述のようなworker functionの実装クラス名のリストを返す
# 上述の例だと、MyApp::Worker::(Foo|Bar|Baz)のリスト
for my $worker_function (@{$self->worker_functions}) {
    $worker->register_function(
        $worker_function,
        MyApp::Gearman::Dispatcher->can('dispatch'),
    )
}

dispatcher

package MyApp::Gearman::Dispatcher;
use strict;
use warnings;
use UNIVERSAL::require;

sub dispatch {
    my $job = shift;
    my $worker_class = $job->{func};
       $worker_class->require or die $@;
       $worker_class->new->work($job);
}

!!1;

こんな感じでdispatcherを挟んでおけば、worker functionを普通のクラスとして、以下のような感じで書ける。

package MyApp::Worker::Foo;
use stirct;
use warnings;
use base qw(MyApp::Worker);

sub work {
    my ($self, $job) = @_;
    # ... do something ...
}

!!1;

普通のクラス階層の中でfunctionが実装できるので、共通の処理をまとめたり、また、設定ファイルにわかりやすくまとめておいて、worker managerでいっぺんにregister_functionしたりということがしやすくなっていいんじゃないかなーと思っています。