@nqounetです。

Perlで、Redisを使ったメッセージキューシステムのResqueというモジュールを使ってみたのでメモしておきます。

前置き

沢山のリクエストを処理するようなウェブアプリでは、少し時間のかかる処理をジョブキューやメッセージキューというようなキューシステムを使って処理することが多くなるでしょう。

個人的にはQudoが好きでよく使っているのですが、NoSQLを使ったものはないのだろうかと探してみたら、Resqueというモジュールを見つけたので使ってみました。

ファイル群

キューシステムは、シンプルにしてもファイル数が多くなるので、慣れないうちは理解するのが大変だと思いますが、コレも慣れです。

1
2
3
4
5
6
7
├── client.pl
├── cpanfile
├── lib
│   └── MyTask
│   └── Echo.pm
├── main.pl
└── worker.pl

それぞれの役目

cpanfile

cpanfileには、モジュールの依存情報、平たく言うと使っているモジュールを書いておきます。

このファイルを作っておくと、モジュールのインストールが簡単です。

cartonを使わない場合でも、cpanm --installdeps .のようにすると、依存モジュールをインストールしてくれます。

1
2
3
4
5
requires 'Log::Minimal';
requires 'Path::Tiny';
requires 'Proclet';
requires 'Resque';
requires 'Test::RedisServer';

client.pl

client.plは、キューを作成する、平たく言うと仕事を取ってくる役目を担います。

会社で言うと、営業の方ですかね。仕事を取ってきて、キューに入れるところまでが役目です。

書いていてちょっと気になったのは、argsが配列のリファレンスしか受け付けないことです。

こういう時は、なんとなくハッシュリファレンスを渡したくなるのですが、受け付けてくれません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env perl
use utf8;
use strict;
use warnings;
use Resque;
use Log::Minimal;


my $resque = Resque->new;

my $count = 10;
while ($count--) {
$resque->push(
echo => +{
class => 'MyTask::Echo',
args => +[+{count => $count}]
}
);
infof($count);
sleep 2;
}

worker.pl

worker.plは、キューを処理する、平たく言うと実際に仕事を片付けるのを担当します。

キューに仕事がある限り、順番に処理するのが役目です。

use lib 'lib';しているのはちゃんと理由があります。

worker.plは、ワーカーのクラスを自動的にロードして使ってくれるのですが、今回のディレクトリの構成では、libの中にワーカーのクラス(MyTask::Echo)があるので、予めライブラリがlibにあることを書いておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env perl
use utf8;
use strict;
use warnings;
use Resque;
use Log::Minimal;
use lib 'lib';


my $worker = Resque->new->worker;

$worker->add_queue('echo');
infof('### start worker');
$worker->work;
infof('### stop worker');

lib/MyTask/Echo.pm

Echo.pmは、実際の処理の内容です。

client.plで、指定しているMyTask::Echoは、このファイルの事を指しています。

worker.plは、このファイルのような仕事の手順書があれば、どのような処理でも実行できます。

なお、performは、worker.plが呼び出す関数名です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package MyTask::Echo;
use utf8;
use strict;
use warnings;
use Log::Minimal;


sub perform {
my $job = shift;
debugf('dequeue: %s', ddf($job->args));
infof($job->args->[0]{count});
}

1;

main.pl

Procletを使って、client.plworker.plRedisを同時に動かします。

worker.plを複数動作させたい場合は、Procletのworkerの数を変更するだけなので、いろいろ簡単に試せて便利ですね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env perl
use utf8;
use strict;
use warnings;
use Path::Tiny qw(path tempdir);
use Test::RedisServer;
use Proclet;


my $redis = Test::RedisServer->new(
auto_start => 0,
tmpdir => tempdir(),
);
$ENV{REDIS_SERVER} = $redis->connect_info;

my $proclet = Proclet->new(color => 1);
$proclet->service(
code => sub {
$redis->exec;
},
worker => 1,
tag => 'redis'
);
$proclet->service(
code => [qw{carton exec -- perl worker.pl}],
worker => 5,
tag => 'worker'
);
$proclet->service(
code => [qw{carton exec -- perl client.pl}],
worker => 1,
tag => 'client'
);
$proclet->run;

使い方

当然ですが、Resqueを試すにはRedisをインストールしておく必要があります。

Macでhomebrewを使っている方は、ターミナルからbrew install redisと入力するだけです。

その他の方は、Redisを入れるか、homebrewを入れるか、Macを買うかしてください。

全部コピペしたら、cpanfileのあるディレクトリで、carton installと入力してください。

必要なモジュールがインストールされます。

cartonが無い場合は、その前にcpanm Cartonと入力してください。

cpanmが無い場合は、その前にcurl -L https://cpanmin.us | perl - App::cpanminusと入力してください。

モジュールのインストールが終わったら、carton exec -- perl main.plと入力してみましょう。

勝手に色々動き始めます。

ジョブキューが初めての方は、main.plに書いてあるworkerclientworkerを増減してみたり、client.plsleepを消したり、数値を変更したり、色々試してみてください。

1
2
3
$ brew install redis
$ carton install
$ carton exec -- perl main.pl

使ってみての感想

RDBMSの場合、少なくともテーブル、場合によってはユーザーやデータベースなどを作る必要があり、ちょっと触ってみるにしても結構面倒なところがあります。

Qudoは、そういう部分のヘルパーもあり、楽チンではあるのですが。

Redis+Resqueの場合は、そのあたりが何も必要ないので、とても簡単です。

ちょっと使ってみたり、ジョブキューに触れて見るにはちょうど良いと思います。

参考資料