Laravelで非同期処理を実装する


Laravelのキューサービス

Laravelのキューサービスは、Amazon SQS、Redis、さらにはリレーショナル・データベースなどさまざまなキューバックエンドに対し共通のAPIを提供しています。キューによりメール送信のような時間を費やす処理を遅らせることが可能です。時間のかかるタスクを遅らせることで、よりアプリケーションのリクエストをスピードアップさせます。

Queueドライバー

データベース

データベースキュードライバーを使用するには、ジョブを記録するためのテーブルを用意する必要があります。
queue:tableコマンドで簡単に生成できます。
php artisan queue:table
php artisan migrate
.envのQUEUE_CONNECTIONdatabaseに変更してください。

Redis

Redisをドライバーに用いるためには、Redisのデータベースを設定する必要があります。
dockerでRedisのコンテナを作成するには、以下が参考になりました。
https://zenn.dev/marushin/articles/4903cd0bbbcee0

.envのQUEUE_CONNECTIONredisに変更してください。

Jobクラスの作成

Jobクラスは、非同期処理を行うためのクラスです。
全てのジョブは、デフォルトでapp/Jobsに生成されます。
php artisan make:job {クラス名}で簡単にJobクラスを生成することができます。

<?php

namespace App\Jobs;

use App\AudioProcessor;
use App\Podcast;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

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

    protected $podcast;

    /**
     * 新しいジョブインスタンスの生成
     *
     * @param  Podcast  $podcast
     * @return void
     */
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    /**
     * ジョブの実行
     *
     * @param  AudioProcessor  $processor
     * @return void
     */
    public function handle(AudioProcessor $processor)
    {
        // ジョブが実行される際に呼び出される処理…
    }
}

上記が生成されたJobクラスです。
この中のhandleメソッド内が、キューによってジョブが実行される際に呼び出されます。

Jobを呼び出す(ディスパッチ)

dispatch

ジョブクラス自身のdispatchメソッドを使い。ジョブを実行させます。dispatchメソッドへ渡す引数は、ジョブのコンストラクタへ渡されます。

class PodcastController extends Controller
{
    /**
     * 新ポッドキャストの保存
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // ポッドキャスト作成…
        ProcessPodcast::dispatch($podcast);
    }
}

dipatchIf・dipatchUnless

条件によりジョブをディスパッチする場合は、dispatchIfdispatchUnlessを使います
第一引数に条件、それ以降にJobクラスのコンストラクタに渡す値を設定します。

ProcessPodcast::dispatchIf($accountActive === true, $podcast);

ProcessPodcast::dispatchUnless($accountSuspended === false, $podcast);

遅延ディスパッチ

ジョブの実行を遅らせたい場合は、ジョブのディスパッチ時にdelayメソッドを使います。
この例では、5分後に処理を行うように設定しています。

ProcessPodcast::dispatch($podcast)
                ->delay(now()->addMinutes(5));

dispatchAfterResponse

ユーザーにレスポンスを送った後に実行されるdispatchAfterResponseメソッドがあります。

dispatchNow

ジョブを非同期でなく、同期的に実行させたい場合は、dispatchNowメソッドを使用します。

キューを用いたカテゴライズ。

ジョブを異なるキューへ投入することで「カテゴライズ」できます。
dispatchメソッドの後にonQueueメソッドを付けることによってカテゴライズします。

 ProcessPodcast::dispatch($podcast)->onQueue('processing');

ワークプロセスの実行

キューに貯められたジョブを実行するには、queue:workプロセスを実行する必要があります。
queue:workコマンドが起動したら、手動で停止するか、ターミナルを閉じるまで実行し続けることに注意してください。
php artisan queue:work

最大時効回数の設定

ジョブが試行する最大回数を指定するには、--triesスイッチを使用します。
または、ジョブクラスに、$triesを定義することで設定することも可能です。

タイムアウトの設定

ジョブの最大実行秒数を指定するために、--timeoutスイッチを使用します。
php artisan queue:work --tiimeout=30
または、ジョブクラスに、$timeoutを定義することで設定することも可能です。

再試行までの時間の設定

失敗してから再試行するまでに何秒待てばよいかを指定するために、--delayスイッチを使用します。
この場合、処理が失敗してから30秒後に再試行します。
php artisan queue:work --tries_3 --delay =30
または、ジョブクラスに、$retryAfter を定義することで設定することも可能です。

queue:listen

queue:work以外でジョブを実行させる以外に、queue:listenコマンドを使用することもできます。
queue:listenコマンドを使えば更新したコードを読み込みたい場合に、手動でワーカをリスタートする必要がなくなります。
しかし、動作のスピードがqueue:workより悪くなってしますデメリットもあります。

queueを指定する

指定した接続の特定のキューだけを処理するように、さらにキューワーカをカスタマイズすることもできます。
php artisan --queue=upload
この場合、dispatch()->onQueue('upload')でディスパッチしたジョブのみが実行されます。

キューのデプロイ

キューワーカは長時間起動プロセスであるため、リスタートしない限りコードの変更を反映しません。キューを使用しているアプリケーションのコードの変更を取り込む簡単な方法は、ワーカをリスタートすることです。queue:restartコマンドを実行することで、全ワーカーを再起動できます。
php artisan queue:restart

失敗したジョブ

失敗したジョブは、fialed_jobテーブルに挿入されます。
こちらも、queue:failed-tableコマンドで簡単に作成できます。
php artisan queue:failed-table
php artisan migrate

失敗した時の処理を書く

ジョブクラスにfailedメソッドを定義すると、ジョブが失敗したときにfailedメソッド内で書かれた処理が実行されます。

    public function failed(Throwable $exception)
    {
        // 失敗の通知をユーザーへ送るなど…
    }

Supervisor設定

Supervisorを設定することで、workerプロセスを永続化させることができます。
設定方法は、自分が作成した以下のリンクから確認してください!
https://qiita.com/RyujiWatanabe/items/f4b132a9107bf8ba5f4e