がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

「バッチの二重処理の防ぎ方」PHP変

なんかタイトルをあえて近づけてみましたw
前回のお題は「 「バッチファイルの二重起動の防ぎ方」PHPhttp://d.hatena.ne.jp/gallu/20110502/p1 」。
今回は「二重処理の防ぎ方」をやってみます。


ん…分かりやすいところだと例えば「mail送信処理」。
送るmailが「1通あたり1レコードor1ファイル」で、n通保存されていて。
それを「3プロセスくらいで同時進行」で処理をしたい、とか。
そんな「ほんのりした分散処理」で使う手法です。


発端…ってほどでもないのですが。一般的にこの辺は「バッチキュー」とか「キューサービス」とかって名前が多いみたい。
例えば、あま〜ぞんさんだと「Amazon Simple Queue Service」ってのをやってるです。
この辺までくると「サーバをまたいでのキューイング」とかイロイロ凄いのですが。
1サーバ前提くらいなら、昔からあるメッセージキューで足りるし。温故知新的に「ふるいネタを把握する」のもいいかなぁ、ってのがおおよその狙い。


まず前提として、仕組みは「FIFO( First In, First Out )」になります。
キューって言い方をしてもよし。
今回のお題の基本であり中心でもあるので、そこは各自把握しておいてください。
http://ja.wikipedia.org/wiki/FIFO
http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A5%E3%83%BC_%28%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%29


で、PHPの実装方法。
まずはメッセージのEnqueue。

// メッセージキューのハンドルをげと
$mh = msg_get_queue(246);

// メッセージを送信(というかEnqueue)する
// 第二引数はとりあえず。
$error_code = '';
$r = msg_send($mh, 1, "string 1", true, true, $error_code);

これで「string 1」というメッセージがキューイングされます。
実際には、ここに「バッチで必要な引数を一式」とか渡してやるとよいと思うです。
ここはセマフォとかでちゃんと一意にして、お仕事発注口を一本に絞って一気に積み上げましょう。


んで、メッセージのDequeue。

// メッセージキューのハンドルをげと
$mh = msg_get_queue(246);

// メッセージを受信(というかDequeue)
// 第二引数は0で「無差別にメッセージをむさぼる」w
$msgtype = 0;
$error_code = '';
$message = '';
$r = msg_receive($mh, 0, $msgtype, 0xffff, $message, true, 0, $error_code);

これで、「string 1」が$messageん中に入ってくるので、実際にはそれをパラメタとして「いそいそとバッチ処理」をすることになるんだろうと思うです。


さて、実はこのままだと問題が。
デキューのタイミングで「メッセージがない」と、msg_receiveの関数、メッセージが来るまで延々と待ち続けます。
いやこれがC言語とかで「メモリリークなしリソース管理はバッチグー」な状態ならよいのですが、ぶっちゃけ、おいちゃんはPHPをそこまで信用しておりません B-p
まぁ信用の有無はさておくにしても「メッセージがなければそのままないよ〜って言っていただきたい」シーンは多々ございます。
そのときは

$r = msg_receive($mh, 0, $msgtype, 0xffff, $message, true, 0, $error_code);

$r = msg_receive($mh, 0, $msgtype, 0xffff, $message, true, MSG_IPC_NOWAIT, $error_code);

にすることで「メッセージがなければ即座にエラー」にしてくれます。戻り値にfalseが帰ってくるので、雑には、そこで判定をするとよいでしょう。
厳密には「戻りがfalseでerror_codeが所定の定数なら」になるのですが。


あ、そうそう。
上述は「メッセージ用のkey」をべた書きしてますが、どこかconfigとかなんとかで一元管理をしたほうがよいです。
この辺もまた、一つの「随所に関所」。


あとは、「メッセージキューそのものが存在するかどうか」を確認する msg_queue_exists 、メッセージキューを「問答無用で破壊する」msg_remove_queue あたりを捕捉しておきましょう。
…まぁ、共有サーバで使うときとかくらいかなぁ? 最近「1台で複数サービス」とか珍しいので、ぶつかるとかそんなにしょっちゅうある事象ではないと思うのですが、一応。