laravel で ジョブ・キューを行う
- ジョブキューを体験する
- ジョブキューの準備
- コマンドの準備
- ジョブ・キュー実行する
- 失敗時の取り扱い
ジョブキューを体験する
Laravelのジョブとキューの仕組みを作って体験してみる。
WEB-UIを作るとめんどくさいので、コマンド・コンソールからジョブ・キューを作って試す。
プロジェクトの準備
プロジェクトを作る && プロジェクト基本設定
composer create-project laravel/laravel job-queue-example
プロジェクトの初期設定
cd job-queue-example/ sed -r 's/^(VITE|PUSHER|AWS|MAIL|REDIS|DB|MEMCACHE|QUEUE)/#\1/' -i .env sed -e '/DB_CONNECTION/i QUEUE_CONNECTION=database' -i .env sed -e '/DB_CONNECTION/i DB_CONNECTION=sqlite' -i .env touch database/database.sqlite sed '/indent_size/d' -i .editorconfig # インデントはエディタに従う
ジョブキューの準備(テーブル)
php artisan queue:table php artisan queue:failed-table php artisan migrate
ジョブを投入するコマンドを作る
php artisan make:command AddMyJob
ジョブを作る
php artisan make:job MyJob
ソースコードを書く
app/Jobs/MyJob.php
<?php class MyJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct ( protected string $message //このジョブは文字列を受け取る ) {} public function handle (): void { dump($this->message); } }
app/Console/Commands/AddMyJob.php
<?php class AddMyJob extends Command { protected $signature = 'app:add-my-job {message}'; public function handle () { $msg = $this->argument('message');//ジョブに文字列を渡す。 MyJob::dispatch($msg); } }
ジョブを動かす。
ワーカーの起動
php artisan -v queue:work
ジョブの投入
php artisan app:add-my-job 'Hello MyJob'
結果の確認
ワーカ側にジョブの実行結果が表示される
INFO Processing jobs from the [default] queue. 2023-11-28 08:24:49 App\Jobs\MyJob 2 .................... RUNNING "Hello MyJob" // app/Jobs/MyJob.php:19 2023-11-28 08:24:50 App\Jobs\MyJob 2 ............... 23.96ms DONE
ただ、これだけど、まともに使えるとは言い難い。
ジョブを失敗させる。
<?php class MyJob implements ShouldQueue { /* 中略 */ public function handle (): void { throw new \Exception('Error'); } }
再起動して失敗ジョブをテストする。
## 再起動(ワーカー php artisan -v queue:work
ジョブの投入
php artisan app:add-my-job 'Hello MyJob'
結果
INFO Processing jobs from the [default] queue. 2023-11-28 08:30:27 App\Jobs\MyJob 4 .................... RUNNING 2023-11-28 08:30:27 App\Jobs\MyJob 4 ............... 20.65ms FAIL
失敗ジョブの確認
php artisan queue:failed
ジョブは実行されて、jobs テーブルから削除され、failed に移動する。
リトライ機能で再挑戦する。
php artisan queue:retry all
ジョブ・キュのポイント
失敗させてわかるのだが、 ここで問題になるのは、ワーカ再起動が必要な点。
また、デフォルトでワーカがジョブをチェックするのは3秒毎である。このタイミングを伸ばせる
php artisan -v queue:work --sleep=10 # 10s毎にチェック php artisan -v queue:work --sleep=30 # 30s毎 php artisan -v queue:work --sleep=60 # 60s毎
だが、--sleep=60の場合,max_exection_time(default=30sec)を超えてsleepすると停止する。
キューに名前をつける
指定した名前のキューだけを待つ。
キューに投入するときに名前を指定する
<?php class AddMyJob extends Command { protected $signature = 'app:add-my-job {message}'; public function handle () { $msg = $this->argument('message'); // ->onQueue('myQueue'); で名前を指定 MyJob::dispatch($msg)->onQueue('myQueue'); } }
名前が一致しないジョブは処理対象から外される。
php artisan -v queue:work --queue=myQueue
ジョブの構造
ジョブにはペイロードが設定されている。ここに実行するジョブの情報が入っている。
array:10 [ "uuid" => "3135f558-cf36-479c-ad42-cb10cea6f069" "displayName" => "App\Jobs\MyJob" "job" => "Illuminate\Queue\CallQueuedHandler@call" "maxTries" => null "maxExceptions" => null "failOnTimeout" => false "backoff" => null "timeout" => null "retryUntil" => null "data" => array:2 [ "commandName" => "App\Jobs\MyJob" "command" => "O:14:"App\Jobs\MyJob":1:{s:10:"\x00*\x00message";s:11:"Hello MyJob";}" ] ]
ペイロードをみると、commandNameがジョブを実行するクラスで "App\Jobs\MyJob"となっている。 "App\Jobs\MyJob"のインスタンスが、commandに serialize()されて格納されているとわかる。
ここから、Modelは必須ではない。とわかる。
時間のかかるジョブ
class MyJob implements ShouldQueue {
/** 略 **/
public function handle (): void {
sleep(31);
dump($this->message);
}
}
時間のかかるジョブを作ると・・・このように、中断されてしまう。ことがある。
takuya@wsl:job-queue-example$ php artisan -v queue:work INFO Processing jobs from the [default] queue. 2023-11-28 09:10:20 App\Jobs\MyJob 23 .......... RUNNING 2023-11-28 09:11:20 App\Jobs\MyJob 23 .......... 1m FAIL Killed
このあたりを考えると、「時間のかかるジョブ」を後ろに回すと考えるのは、早計であるとわかる。
laravelのジョブの実行時間設定、phpのmax_execution_timeをちゃんと確認してからジョブを回しましょう