Laravelのキューを使ってバッチ処理を制御する

2022年7月1日金曜日

Laravel PHP

t f B! P L

enter image description here

PHPのフレームワークである Laravel には、FIFO(先出先入)でキューを投入し、登録した順にジョブを起動する仕組みがある。この記事では、そのキューを使うための準備と、簡単なジョブの実行方法を解説します。

スポンサーリンク

キューの準備

テーブルの作成(マイグレーションファイルの作成)

php artisan queue:table

上のコマンドを実行すると、database/migrations/xxxx_xx_xx_xxxxxx_create_jobs_table.php という名前でマイグレーションのファイルが作られます。(xxxx_xx_xx_xxxxxxは時間)

マイグレーションの実行

マイグレーションを実行してテーブルを作成する。

php artisan migrate

マイグレーションが終わると、こんな感じで「jobs」テーブルがデータベースに作られる。

enter image description here

「.env」の修正

.env を開いて、QUEUE_CONNECTION の内容を次のように修正する。

- QUEUE_CONNECTION=sync
+ QUEUE_CONNECTION=database

ジョブの作成

環境の準備ができたら、まずキューを受けて実行するジョブを作成する。

ジョブの作成は、php artisan make:job ジョブ名 コマンドで作成する。今回は HogeJob という名前でジョブを作成した。

php artisan make:job HogeJob

app/Jobs に作成された HogeJob.php を編集する。

<?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 Illuminate\Support\Facades\Log;

class HogeJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $payload;

    //コンストラクタでジョブに渡されたパラメータを受け取る
    public function __construct($payload)
    {
        $this->payload = $payload;
    }

    //ジョブが起動された時の処理
    public function handle()
    {
        Log::info("HogeJobが起動されました。 payload={$this->payload}");
    }
}

スポンサーリンク

ジョブの起動(キューの登録)

作成したジョブを起動するには、ジョブクラス名::dispatch でキューを登録する。

HogeJob::dispatch("ジョブに渡すパラメータ");

キューを登録すると、次のように jobs テーブルにキューが登録される。

enter image description here

しかし、このままでは ジョブは実行されない、キューに登録したジョブを起動するには、次の手順でキューワーカーを起動する必要がある。

キューワーカーの起動

登録されたキューを監視し、対応するジョブを起動するのがキューワーカーである。これを起動させておかないと、dispatch でキューを登録しても、キューが貯まり続ける一方で実行されない。

キューワーカーは、次のコマンドで起動できる。

php artisan queue:work

本番環境でキューワーカーを使用する場合は、プロセス永続化ができる「Supervisor」を使用することが公式サイトでもすいしょうされている。

実行ログの確認

Log::info("ログの内容") で出力したログは、デフォルトでは /storage/logs/laravel.log に出力される。HogeJob がキューワーカーによって実行されれば、次のログが出力されるはずだ。

[2022-06-25 01:12:08] local.INFO: HogeJobが起動されました。 payload=ジョブに渡すパラメータ

また、ログはエラーレベルに応じて次の表に示す関数を使い分ける。

メソッド ログレベル
Log::debug() debug
Log::info() info
Log::notice() notice
Log::warning() warning
Log::error() error
Log::critical() critical
Log::alert() alert

失敗したジョブを記録する

失敗したジョブを記録する failed_jobs テーブルを作成しておくと、ジョブで例外が発生した時にレコードを登録してくれる。

次の2つのコマンドでマイグレーションファイルの作成と、テーブルの作成を行う。

php artisan queue:failed-table
php artisan migrate

例外を発生させる

HogeJob.phphandle 関数を次のように編集し、わざと例外が発生するように仕込みを入れる。

    //ジョブが起動された時の処理
    public function handle()
    {
        Log::info("HogeJobが起動されました。");
        throw new Exception('わざとエラー');
    }

ジョブを起動すると、`failed_jobs` テーブルに失敗したジョブのキューが登録される。

enter image description here

キャッチされない例外がジョブで発生した時は、/storage/logs/laravel.log にエラーログが記録されるので、障害発生時には、まずこのファイルを見ることになるだろう。

enter image description here

例外が発生した時の再試行回数を指定する

デフォルトはジョブで例外が発生すると、その時点で failed_jobs にエラーが記録され、再試行はされない。例外発生時にジョブをリトライさせたい場合、ワーカー起動時に --tries オプションを指定する。

例えば、リトライ回数を3回にする場合は、ワーカー起動時のコマンドを次のように書く。

php artisan queue:work --tries=3

失敗したジョブを再実行する

failed_jobs にエラーとして記録されたジョブを再実行する場合は、php artisan queue:retry コマンドを使用します。その際、失敗したジョブの ID を引数に指定します。

php artisan queue:retry 3

enter image description here

失敗したジョブ全てを再実行する

実際の運用で使用するケースは少ないと思われるが、失敗したジョブ全てを再実行することも可能。

failed_jobs に溜まっている失敗したジョブを全て再実行する場合は、queue:retry の引数に all を指定する。

php artisan queue:retry all

スポンサーリンク

補足:パラメータにオブジェクト(クラス)を渡す

ジョブへのパラメータに、クラスなどのオブジェクトを渡すこともできる。
オブジェクトを渡すために必要な特別な手順はなく、普通に引数に指定できる。

サンプルコード

JobParameter というクラスを作り、ジョブのパラメータに渡してみる。

class JobParameter
{
    public $param1;
    public $param2;
}

コントローラー側では、 JobParameter のインスタンスを作り、dispatch メソッドの引数に渡す。

$param = new JobParameter();
$param->param1 = "テスト 太郎";
$param->param2 = "test@excample.com";
HogeJob::dispatch($param);

ジョブのクラス側では、コンストラクターで渡された JobParameter を受け取る。

class HogeJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $jobParam;

    public function __construct(JobParameter $jobParam)
    {
        //コントローラーから渡された JobParameter クラスを受け取る
        $this->jobParam = $jobParam;
    }

    //ジョブが起動された時の処理
    public function handle()
    {
        //ログに JobParameter の内容を出力
        Log::info("param1={$this->jobParam->param1}");
        Log::info("param2={$this->jobParam->param2}");
    }
}

これを実行すると、ログに次の内容が出力され、コントローラーからパラメータが正常に渡っていることが分かる。

[2022-06-25 12:13:32] local.INFO: param1=テスト 太郎  
[2022-06-25 12:13:32] local.INFO: param2=test@excample.com  

スポンサーリンク

コマンド経由でジョブを起動(キューを登録)する

Laravelでは、コマンドインターフェイスから、Laravel プロジェクト内で作成したクラスを実行するカスタムコマンドが作れる。Webアプリ側で作成した処理を、コマンドラインから呼び出される処理でも共有でき、バッチ処理の開発効率を上げてくれる便利なやつだ。

このコマンドを使用して、シェルなどのコマンドラインツールからキューを登録する処理を作れる。

コマンドの作成

make:command Artisanコマンドを使って新しいコマンドを作成する。

php artisan make:command SampleCommand

すると、app/Console/Commands の下に SampleCommand.php が作成される。

作成されたファイルを編集して、関数内の処理を編集して、コマンドが実行された時に HogeJob のキューを登録する。

<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

class SampleCommand extends Command
{
    protected $signature = 'sample:queue';

    protected $description = 'コマンドでキューを登録するサンプルコード';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        CYBB0010Job::dispatch("ジョブに渡すパラメータ");
        return 0;
    }
}

コマンドの実行

あとは、次のようにして、作成したコマンドをコマンドインターフェイスから起動すれば、上で作成した処理が実行される。

php artisan sample:queue

コマンド経由でジョブ(キュー)を起動できるようにしておくことで、CRONなどのスケジューラーなどからも扱いやすくなる。

引数を受け取るコマンド

引数を受け取るコマンドを作る場合は、$signature の宣言を編集する。

例えば引数に「id」と「name」の2つの引数を受け取るコマンドを作る場合は、$signature を次のように編集する。

protected $signature = 'sample:queue {id} {name}';

渡された引数は、$this->argument("引数名"); で取得可能である。

    public function handle()
    {
        $id = $this->argument("id");
        $name = $this->argument("name");
        Log::info("id={$id}");
        Log::info("name={$name}");
        return 0;
    }

では、引数を付けてコマンドを呼び出してみる。引数はコマンド名の後ろのスペースを開けて書いていく。

php artisan sample:queue 123 YAMADA

コマンドが実行されると、ログに次の内容が出力されているはずだ。

[2022-06-30 15:51:12] local.INFO: id=123  
[2022-06-30 15:51:12] local.INFO: name=YAMADA  

まとめ

Laravel でキューを登録してバッチ処理を実行する方法を解説してきました。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ