はじめに
参考)https://readouble.com/laravel/8.x/ja/queues.html
バッチ処理もいいんだけど、役割分担すると読みやすかったり何かと便利だったりする。
Queue(キュー)
そのうち処理してねってデータを保持する場所かな。
ControllerやCommandからキューにデータを入れる。
Laravelでは、“sync”, “database”, “beanstalkd”, “sqs”, “redis” が使えるらしい。
今回は、“database” を使って動きを見る。
Job(ジョブ)
Queueに入っているデータが渡され、それを使って処理する子。
Worker(ワーカー)
Worker(ワーカー)は常駐していて、Queue(キュー)を監視する
キューがあれば、Job(ジョブ)にデータを渡して処理を実行するイメージ。
設定
まずは、.env
#QUEUE_CONNECTION=sync
QUEUE_CONNECTION=database
参考)Laravel7.xの場合の.env
#QUEUE_DRIVER=sync
QUEUE_DRIVER=database
試しながらやる場合は、syncのほうが良いかも。syncの場合、後述のworkerの起動不要で実行してくれる。ジョブが失敗したとき再実行はsyncではできないような気もする…
データベースを利用するのでテーブルが必要です。
以下のコマンドでjobsテーブルとfailed-tableテーブル(失敗したジョブデータが入るテーブル)ができる。
% php artisan queue:table
% php artisan queue:failed-table
% php artisan migrate
まぁこんな感じでいいのかな。
次はJob(ジョブ)。以下のコマンドでジョブがapp/Jobs/TestJob.phpとして作られる。
% php artisan make:job TestJob
Job created successfully.
ジョブは、コンストラクタとhandleメソッドの2つである。
Queue(キュー)へデータを突っ込む
コントローラでもコマンド(バッチ処理)でもいいので、適当にキューへデータを突っ込んでみる。
TestJob::dispatch([
"id" => 123,
"phone_number" => 'xxx-xxxx-xxxx',
]);
これを実行するとjobsテーブルにレコードが作成される。
TestJobのコンストラクタも呼び出されるので、まだ実行してはいけない。
Job(ジョブ)を作る
とりあえず、コンストラクタとhandleの動きを見るためにログを出力するだけの実装をする。
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Log;
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $data;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($data)
{
Log::info('__construct Begin');
$this->data = $data;
Log::info($this->data);
Log::info('__construct End');
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Log::info('handle Begin');
Log::info($this->data);
Log::info('handle End');
}
}
コンストラクタとhandleの動きを見るためにこんな感じで。ログをtailしておこう。
この段階で、キューへデータを登録する処理(さっき実行してはいけないと書いたところ)を流してみると、ログにコンストラクタの部分が出力されているはず。
Worker(ワーカー)を動かす
最終段階!ワーカーを動かす。
% php artisan queue:work
ログにhandleメソッドが呼び出されたログが出力されているのがわかる。
ワーカーは常駐したまま。
注意点!ワーカーを常駐させたまま、ジョブをhandleの内容を編集しても、常駐時点のソースで実行されるから、ワーカーは再起動が必要!!!(反映されないからかなりハマった(;_;))
もしくは、
% php artisan queue:listen
別の方法として、queue:listenコマンドを実行することもできます。queue:listenコマンドを使えば更新したコードをリロード、もしくはアプリケーションの状態をリセットしたい場合に、手動でワーカをリスタートする必要がなくなります。しかし、このコマンドはqueue:workほど効率はよくありません。
https://readouble.com/laravel/8.x/ja/queues.html
まだ試してないけど、上の注意点が解消されるのかな(゜゜)
例えば、メールとか外部サーバへのAPIでデータ送信したいとかってなると失敗したときにリトライ処理が必要となる。
% php artisan queue:work --tries=3 --delay=5 --timeout=10
これでジョブが失敗したとき、3回リトライする。リトライする時は5秒待機となる。1回のタイムアウトは10秒。
ジョブが少ない時はこれでもいいけど、ジョブごとにリトライ数・待機時間・ジョブのタイムアウトは設定したいと思うこともあるだろう。
リトライ(試行回数)
ジョブに以下のプロパティ設定する。
class TestJob implements ShouldQueue
{
public $tries = 5;
これで5回リトライする。処理の中で実行回数を取得したい場合は、
$this->attempts()
で取得できる。
リトライ時の待機秒数
ジョブに以下のプロパティかメソッドを追加する。
class TestJob implements ShouldQueue
{
public $retryAfter = 5;
または、
public function retryAfter()
{
return 5;
}
これで5秒待機する。
タイムアウト
ジョブに以下のプロパティを追加する。
class TestJob implements ShouldQueue
{
public $timeout = 10;
これで10秒でタイムアウトする。
試行回数がオーバーした場合
例えば3回リトライしてもエラーとなるような場合、failed-tableテーブルにデータが登録される。
これを再実行したい場合は、
php artisan queue:retry 1
とか
php artisan queue:retry all
を実行すると、failed-tableテーブルのデータがjobsテーブルに移動するので、再度ジョブの実行対象となる。
まとめ
バッチで処理するのもありだけど、メールの送信とか時間がかかる処理を順次処理していくとか使っていくのもいい感じだと思う。
サーバ環境でworkerを動かすのはSupervisorで。[Laravel7.x]Supervisorを導入してみる!にまとめてみた。
課題
- 本番環境での自動起動方法
→2021/02/26 追記
Supervisorで起動すればいいらしい。
→2021/05/21 追記
参考)
https://qiita.com/Hiro2525/items/7b47311e06c63e40a46c
https://readouble.com/laravel/8.x/ja/queues.html - エラー発生時の実装サンプル(failed-tableテーブル)
→2021/05/20 追記 - AWSのSQSを使ってみる。
コメント