こんにちは、tatsumiです。
システム開発を行っていると、非同期処理を実装する場面は少なくないと思います。
ということで、今回は私自身の備忘録も兼ねて、Laravelでの非同期処理についてまとめてみました。
Laravelで非同期処理を行うには?
Laravelで非同期処理を行う場合、キューを利用します。
まず、キューを利用する上での登場人物3名を紹介します。
キュー | キューはある決まった処理を非同期で実行するための仕組みのことです。 キューにジョブを登録し、登録されたジョブから実行されていきます。 |
---|---|
ジョブ | ジョブは実行する処理自体のことを指します。 |
ワーカー | ワーカーはジョブを処理するプロセスのことを指します。 |
キューについて理解していない方は、スーパーのレジを想像してみてください。
レジで順番待ちをしている人の列がキューになります。
また、一人一人の会計処理がジョブを指し、レジ打ちをしている店員がワーカーとなります。
思い返せば、飲食店の行列や役所や病院での待ちもキューで、日常生活の中にも多く存在しています。
キューとキューを利用する上での登場人物について理解したところで、早速実際にLaravelでキューを使ってみましょう。
Laravelでキューを使ってみよう
キュー用のテーブルを作成する
まずはLaravelでキューを利用する際に必要となるテーブルを作成しましょう。
テーブルの作成は下記コマンドで行うことができます。
$ php artisan queue:table $ php artisan migrate
コマンドを実行すると、jobsテーブルとfailed_jobsテーブルが作成されます。
それぞれのテーブルの役割は以下の通りです。
jobs | キューに登録されているジョブを管理します。 ジョブが登録されるとjobsテーブルに登録され、テーブルに登録されているジョブのうち、古いものから順に処理されます。 |
---|---|
failed_jobs | ジョブ実行の失敗を記録します。 ジョブの処理が何等かの理由で失敗した際に、failed_jobsテーブルに登録されます。 合わせてexceptinカラムにジョブ実行時に発生したexceptionとスタックトレースが出力されます。 |
ジョブクラスを作成する
テーブルの用意ができたら、次にジョブクラスを作成しましょう。
ジョブクラスの作成は下記コマンドで行うことができます。
$ php artisan make:job SampleJob
実行すると、app/Jobs
フォルダの中にSampleJob.php
が作成されます。
作成されたクラスには、__construct()
メソッドとhandle()
メソッドがあり、それぞれの役割は以下の通りです。
__construct() | ジョブをキューに投入する際にジョブに対して引数を渡すことができます。 (キューへジョブを投入する方法は後述します) その引数を当メソッドで受け取ります。 |
---|---|
handle() | ジョブ上で実行したい処理を記述します。 |
上記の他にfailed()
メソッドでジョブ処理失敗時の処理を定義することができます。
ジョブ処理内で何等かのエラーが発生した際に、failed()
メソッドが呼び出され、引数としてジョブの失敗の原因となったThrowableインスタンスが渡されます。
ジョブクラスのタイムアウト時間/試行回数を設定する
ジョブクラスでは、ジョブのタイムアウト時間や試行回数を指定することもできます。
・タイムアウト時間
ジョブ1回あたりのタイムアウト時間を指定できます。
指定する場合は、クラス変数として$timeout
を定義します。
(指定しない場合は、デフォルト値の60秒となります。)
public $timeout= タイムアウト時間(秒数指定);
※指定した秒数でジョブ処理が終了しなかった場合は、失敗扱いとなります。
・試行回数
ジョブが失敗した際に、何回まで再試行するかを指定できます。
指定する場合は、クラス変数として$tries
を定義します。
(指定しない場合は、1度失敗すると再試行は行われずに即失敗扱いとなります。)
public $tries = 試行回数;
※試行回数上限に達した場合は、failed()
メソッドが呼び出され、ジョブ失敗扱いとなります。
(failed_jobsテーブルに登録される)
タイムアウト時間/試行回数はコマンドでも指定可能ですが、クラス変数の値が優先されるため注意!
キューにジョブを投入する
ジョブの実装が完了したら、いよいよ作成したジョブをキューに投入してみましょう。
キューにジョブを投入するには、dispath()
メソッドを使います。
SampleJob::dispatch($Jobへの引数)
これで作成したジョブがキューに投入され、ワーカーによってジョブに実装した処理が実行されるようになります。
キューを非同期実行に変更する
デフォルトでは、キューは同期実行されるようになっているため、非同期実行とするために.env
の設定を変更します。
QUEUE_CONNECTION=database QUEUE_DIRVER=database
これで基本的な準備は完了です!
あとは実際に動かして、動作を確認してみましょう。
おまけ
ここからはおまけです!
dispath()
メソッドでキューにジョブを投入すると、投入した分だけキューにジョブが溜まっていきます。
しかし、キューの中に同じジョブは常に一つだけにしたい!というケースもあると思います。
そんなときは、一意なジョブを作成することで解決できるので、おまけでは一意なジョブの作成方法について紹介します。
ShouldBeUniqueインターフェイスを実装する
まず一つ目の方法は、ジョブクラスにShouldBeUnique
インターフェイスを実装する方法です。
class SampleJob implements ShouldQueue, ShouldBeUnique { ・・・ }
上記のように、ジョブクラスに対してインターフェイスを実装するだけです。 このインターフェイスでは、ジョブクラスへメソッドを追加する必要はありません。
このインターフェイスを実装すると、対象のジョブがキューに存在している、または処理中のタイミングでdispath()
メソッドを実行しても、キューに投入されません。
ジョブの処理が完了した後、または再試行にすべて失敗した後、一意のジョブがロック解除され、キューに対象のジョブを投入できる状態となります。
もし、ジョブが実行される直前にロックを解除したい場合は、ShouldBeUnique
ではなくShouldBeUniqueUntilProcessing
インターフェイスを実装しましょう。
uniqueIdを定義する
二つ目の方法は、ジョブクラス内で特定の「キー」を定義する方法です。
public function uniqueId() { return 任意のID; }
上記のように、ジョブクラスにuniqueId()
メソッドを実装します。
このメソッドを実装すると、先ほどのShouldBeUnique
インターフェイスを実装したときと同じように、同じIDを持つジョブは既存のジョブの処理が完了するまで`dispath()``メソッドを実行しても、キューに投入されません。
また、時間経過でジョブの一意ロックを解放することもできます。
その場合は、ジョブクラスにuniqueFor
プロパティを定義します。
既存のジョブがuniqueForで指定した時間内に処理されない場合、一意ロックが解放され、同じキーを持つ別のジョブをキューに登録できるようになります。
public $uniqueFor = ジョブの一意ロックが解放されるまでの秒数;
さいごに
本記事では、Laravelでのキューを利用した非同期処理についてまとめてみました。 ここで紹介した内容は、Laravelの公式ドキュメントにも載っているので、是非そちらも参照してみてください!
それでは良きPHPライフを!
エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
https://career-recruit.rakus.co.jp/career_engineer/
カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.com
ラクスDevelopers登録フォーム
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/
イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
◆TECH PLAY
techplay.jp
◆connpass
rakus.connpass.com