Laravel

「Lumen」で「Job」を使用して非同期処理

API開発時に大量のメッセージを送る等といった時間がかかる処理があり、この処理を非同期で行うようにしました。
Lumenで非同期処理を行う場合「Listener」と「Job」を使うケースがありましたが、今回はイベントで行わず「Job」を使うことにしました。
「Job」を使用する場合に「Laravel」での使用情報は多かったのですが、「Lumen」で使用する場合について記載が少なかったのでまとめました。

環境

  • Lumen 9.x
  • Docker (PHP環境)

準備

今回のケースではデータベースにキューを登録して、裏で実行する様にするためにテーブルを作成する必要があります。

「jobs」と「failed_jobs」テーブルを作成する

キューを保存する「jobs」テーブルのマイグレーションファイルは「artisan」コマンドで作成することができます。

$ php artisan queue:table

またキューの処理が失敗した場合に保存する「failed_jobs」テーブルのマイグレーションファイルは以下のコマンドで作成します。

$ php artisan queue:failed-table

以下のコマンドで上記のテーブルを作成します。

$ php artisan migrate

「.env」ファイルの変更

.env」の「QUEUE_CONNECTION」の箇所を「database」に書き換えます。

...省略
QUEUE_CONNECTION=database

「Job」プログラムを実行する

「Lumen」の9ではプロジェクト作成時にすでに「Jobs」フォルダがあり、その中に「Job.php」と「ExampleJob.php」があります。
他に処理を行う場合「ExampleJob.php」をコピーして書き換えるといいかと思います。
まずは「SampleJob.php」を作成します。

<?php

namespace App\Jobs;

class SampleJob extends Job
{
    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
        sleep(10);
        \Log::debug("SampleJob");
    }
}

処理の内容は、10秒経過してデバッグログだけを書き出すようになります。
使用する場合は以下のように使用します。

<?php
...省略

// import Queue
use Queue;

...省略
Queue::push(new SampleJob);

上記の処理でデータベースの「jobs」にキュー情報を保存するようになりますが、まだ処理の実行はされません。
そこで「jobs」を監視してデータベースにキューが保存された場合に処理を実行する様に「artisan」コマンドを実行します。

$ php artisan queue:work

キューが登録されていると以下のように処理を実行します。

2023-03-16 14:42:40 App\Jobs\SampleJob ................................................................................................................................................... RUNNING
2023-03-16 14:42:41 App\Jobs\SampleJob ........................................................................................................................................... 1,011.20ms DONE

「Job」に値を渡す方法

また以下のようにすれば値を渡した状態でキューを登録することができます。

<?php

namespace App\Jobs;

class SampleJob extends Job
{
    public $message;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($message)
    {
        //
        $this->message;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
        sleep(10);
        \Log::debug("SampleJob" . $this->message);
    }
}

キューを登録する場合は値を渡して呼び出します。

<?php
...省略
// import Queue
use Queue;

...省略
$sampleJob = new SampleJob("message");
Queue::push(new $sampleJob);

「queue:work」を監視する

queue:work」は実行後に待機状態となって、キューを監視し続けます。
ただ勝手に終了するケースもあり、止まった場合に裏で動くキューが動作しなくなることがあります。
そこで「supervisor」を使用して「queue:work」を監視し続ける必要があります。

$ apt-get install supervisor

インストール後に以下の場所に設定ファイルを作成します。

$ vi /etc/supervisor/conf.d/laravel-worker.conf

設定ファイルの内容は以下のようになります。

[program:laravel-worker] 
process_name=%(program_name)s_%(process_num)02d 
command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 
autostart=true 
autorestart=true 
user=root 
numprocs=2 
redirect_stderr=true 
stdout_logfile=/var/www/html/storage/logs/worker.log 

command」の箇所は「artisan」ファイルが入ってる箇所と設定します。
stdout_logfile」は「lumen」のログファイルを保存する場所となります。

設定ファイルを作成して以下のコマンドで「supervisor」を起動します。

$ service supervisor start
Starting supervisor: supervisord.

起動後に以下のコマンドで起動しているかどうかを確認します。

$ service supervisor status
supervisord is running

OS再起動時にも「supervisor」が起動出来るように設定します。

$ service supervisor enable
Usage: /etc/init.d/supervisord {start|stop|restart|force-reload|status}

これで一通りの設定が完了しました。
メールの送信や外部APIに対して返答に影響されない処理といった時間がかかるものは非同期で実行するとよいです。
「Event」と「Listener」を使用した方法もありますが、簡潔に非同期処理を行いたい場合は「Job」で実行するとよいかと思われます。