RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Laravelでの非同期処理についてまとめてみた

こんにちは、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の公式ドキュメントにも載っているので、是非そちらも参照してみてください!

readouble.com

それでは良きPHPライフを!


エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
20210916153018
https://career-recruit.rakus.co.jp/career_engineer/

カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.com

ラクスDevelopers登録フォーム
20220701175429
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/

イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!

◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.