GearmanをPHPから使ってみた。

今回は、PHPでGearmanを使えるようにして、ざっと動きを確認してみました。
Gearmanの設定はこっち
ちなみに、よく知らんのだけど、Gearmanは(GearmanClient::doのように)即時実行で結果をclientに返す用途がメジャーなのかな?
ほかのサイトだと、そういう風に紹介されてました。
GearmanCient::doBackgroundをつかうと、TheSchwartzみたい?に
ジョブを突っ込むだけ突っ込んであとはお願いね〜ということができるので
今回はこっちを使います。


はい、まず Gearman PHP Extensionを入れます。

yum install php-devel
wget http://pecl.php.net/get/gearman-0.7.0.tgz
tar xzf gearman-0.7.0.tgz
cd gearman-0.7.0
phpize
./configure
make
make install

OK。


php.iniにextensionの記述を追加します。
CentOSだと/etc/php.d/以下にiniを置けばOKぽいのでそうしてみます。

# iniファイルつくって
echo 'extension=gearman.so' | cat > /etc/php.d/gearman.ini
# apache再起動
service httpd restart
# 動いてるかな?
php -r 'print gearman_version();'

僕の環境だと、"0.14"って出力がでました。わーい


さて、さっそくテスト。
まず、worker。
ただ、printするだけ。
worker.php

<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('test', 'my_test');
while ($worker->work());

function my_test($job) {
        print $job->workload()."\n";
}


clientもつくる
client.php

<?php
$client = new GearmanClient();
$client->addServer();
$time = time();
print $time."\n";
$client->doBackground("test", $time);
if ($client->returnCode() != GEARMAN_SUCCESS) {
    print "faild add job!\n";
}

できた。

$ php worker.php

でworkerを起動。
別のterminalを開いて

$ php client.php

を実行するとそれぞれ

$ php worker.php
1294802686
$ php client.php
1294802686

となります。ちゃんとjobが渡ってますね。


さて、ここから障害時にどうなるかをちょっとみてみました。

workerが死んでたらどうなるの?

$ php client.php 
1294802944
$ php client.php 
1294802946

clientは問題なく動きます。

select * from queue;
+--------------------------------------+---------------+----------+------------+
| unique_key                           | function_name | priority | data       |
+--------------------------------------+---------------+----------+------------+
| 713ed7c0-7e51-4e9a-8f99-76f66693bfd6 | test          |        1 | 1294802946 | 
| 349f796f-6a00-4ef1-ba81-48a3b2a52468 | test          |        1 | 1294802944 | 
+--------------------------------------+---------------+----------+------------+
2 rows in set (0.00 sec)

mysqlにも溜まってますね。
ここで、workerを動かすと

$ php worker.php 
1294802944
1294802946

jobが消化されました。
ここまでは、ジョブキューとして当然の動きですね。

gearmandが死んでたら?

$ php client.php 
1294803188
PHP Warning:  GearmanClient::doBackground(): gearman_connection_flush:could not connect in /home/toshiyuki/gearman_test/client.php on line 6
faild add job!

もちろん、キューに突っ込めないので、clientがエラーになります。


workerを動かすと

$ php worker.php

お、何ともなく動く。
じゃあ、ここで、gearmandを起動。

 gearmand -q libdrizzle --libdrizzle-db=gearman_queue --libdrizzle-table=queue --libdrizzle-user=root -u root -d
$ php worker.php
1294803787
1294803789
1294803789
1294803790

お!来た!! これは想定外。動かないと思ったよ。
gearmand起動から、job実行まで5秒くらいかかりますが、ちゃんと動きました!


ということで、clientのdoBackgroundで例外補足したら、gearmandを起動させて、再度doBackgroundをすれば障害時にも何とかなりそうです。
workerはその際についでに生きてるか確認するか、cronで確認すればOKでしょう。

<?php
function errorHandler($no, $msg, $file, $line) {
        throw new Exception($msg, $no);
}
set_error_handler('errorHandler');

$client = new GearmanClient();
$client->addServer();
$time = time();
print $time."\n";
$try = 0;
while ($try < 3) {
    $try++;
    try {
        $client->doBackground("test", $time);
        break;
    } catch (Exception $e) {
        print "start gearmand!\n";
        system('gearmand -q libdrizzle --libdrizzle-db=gearman_queue --libdrizzle-table=queue --libdrizzle-user=root -u root -d');
        print "wait: ".($try*$try)."s\n";
        sleep($try*$try);
    }
}
if ($client->returnCode() != GEARMAN_SUCCESS) {
    print "faild add job!\n";
}
$ php client.php 
1294804988
start gearmand!
wait: 1s

わーい入れれた!