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

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

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

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

.envの`QUEUE_CONNECTION`を`redis`に変更してください。

## 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
条件によりジョブをディスパッチする場合は、`dispatchIf`か`dispatchUnless`を使います
第一引数に条件、それ以降に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`コマンドを使用することもできます。
q`ueue: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