いけむランド

はてダからやってきました

queue:work --daemon をきちんと動かしてみる

以前 qiita で公開していた記事を引っ越してきたものです。最終更新からかなり経っているため、情報の正確性は落ちている可能性があります。

Laravel の queue を daemon で動かすにはぐぐってみると Supervisor を使っている事例が多く見つかる。

supervisord.org

しかし、queue:listen (または queue:work --daemon) が pop した worker job を処理している最中でも、遠慮なく SIG* で終了させてしまう実装になっているように見えるため、タイミングによっては worker job が適切に完了されない場合もある。

ちなみに Laravel の queue は 4.2 の実装では 2 種類存在する。

  • queue:listen は pop した worker job ã‚’ Symfony/Component/Process/Process 経由で proc_open し、子プロセスとして処理する。
  • queue:work --daemon は自身のプロセス内で都度 worker job ã‚’ pop して実行する。

ただし、後者は worker job が終了する毎に queue:restart で終了命令が発行されていないか確認して、されている場合は queue:work --daemon 自身を終了するようになっている。

そこで queue:restart を使って、実行中かもしれない worker job を SIG* で強制終了させることなく queue:work --daemon を再起動させる仕組みを考えてみた。

Laravel の Command の fire() で以下の実装をしてみる。

// start.php
$pidFile = '/var/run/laravel-queue-work-daemon.pid';

if (File::exists($pidFile)) {
  return;
}

$pid = pcntl_fork();

if ($pid === -1) {
  exit();
} elseif ($pid !== 0) {
  exit();
} else {
  posix_setsid();
  File::append($pidFile, posix_getpid() . PHP_EOL);
  $this->call('queue:work', [
    '--daemon' => true
  ]);
}
// stop.php
$pidFile = '/var/run/laravel-queue-work-daemon.pid';

if (! File::exists($pidFile)) {
  return;
}

$pid = intval(File::get($pidFile));

$this->call('queue:restart');

$procPath = '/proc/' . $pid;
while (true) {
  if (! File::exists($procPath)) {
    break;
  }
  sleep(1);
}

File::delete($pidFile);

これで daemon を起動する時は

$ /usr/bin/nohup php artisan myqueue:start < /dev/null > /dev/null 2>&1 &

終わらせる時は

$ php artisan myqueue:stop

とそれぞれ入力すればいい。