バックエンドの実装に関して、時間がかかる処理を行う場合、非同期に処理したいケースがあると思います。今回はLaravelで非同期に処理するための手軽な方法を紹介します。
本記事ではPHP 7.2、Laravel 5.5の環境を想定しています。
非同期に処理するに当たりジョブのキューを管理する方法としては、データベースを利用する方法やRedisを利用する方法などがあります。Laravelでは標準でデータベース、Redis、Amazon SQS、Beanstalkdを利用することができますが、今回は最も手頃なデータベースを使用する実装を紹介します。
以下のコマンドを実行してキューを管理するためのテーブルを作成します。
$ php artisan queue:table $ php artisan migrate
このコマンドを実行すると、以下ようなテーブルが作成されます。
mysql> show columns from jobs; +--------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | queue | varchar(255) | NO | MUL | NULL | | | payload | longtext | NO | | NULL | | | attempts | tinyint(3) unsigned | NO | | NULL | | | reserved_at | int(10) unsigned | YES | | NULL | | | available_at | int(10) unsigned | NO | | NULL | | | created_at | int(10) unsigned | NO | | NULL | | +--------------+---------------------+------+-----+---------+----------------+
ドライバはデフォルトでは sync になっていますので、 database に変更します。
QUEUE_DRIVER=database
それでは、非同期で処理するジョブを実装していきます。ユーザー登録時に何らかの処理を行う想定です。なお、ユーザーテーブルは既に定義してあるものとします。
以下のコマンドでジョブクラスを作成します。作成したクラスは app/Jobs ディレクトリに配置されます。
php artisan make:job AfterUserRegister
このコマンドで作成されるジョブクラスを見てみましょう。
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; class AfterUserRegister implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct() { // } public function handle() { // } }
見て分かる通り、ジョブクラスは実行時に呼び出されるhundleメソッドのみあります。なお、ジョブクラスにはEloquentモデルを渡すことができます。ディスパッチ時に渡される引数は以下のようにコンストラクタで取得できます。
<?php namespace App\Jobs; use App\User; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; class AfterUserRegister implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $user; public function __construct(User $user) { // dispatchメソッドの引数でわたされる $this->user = $user; } public function handle() { // ジョブで処理する内容 } }
ジョブのディスパッチには実装したジョブクラスの dispatch メソッドで行います。以下はコントローラでディスパッチする例です。
<?php namespace App\Http\Controllers; use App\User; use App\Jobs\AfterUserRegister; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UsersController extends Controller { public function store(Request $request) { // ユーザ作成 $user = User::create($request->all()); AfterUserRegister::dispatch($user); } }
キューを処理するためにはキューワーカーを起動する必要があります。以下のコマンドでキューワーカーを起動します。
$ php artisan queue:work
なお、キューワーカーは長時間起動しっぱなしのプロセスなので、コードを変更した場合はリスタートする必要があります。キューワーカーのリスタートは以下のコマンドで行います。
$ php artisan queue:restart
ここまでで非同期にジョブを処理することができるようになりました。ここからは、より細かな制御について紹介します。
ディスパッチするキューを指定することで、優先的に処理することがなどが可能です。キューの指定には onQueue メソッドを使います。
<?php namespace App\Http\Controllers; use App\User; use App\Jobs\AfterUserRegister; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UsersController extends Controller { public function store(Request $request) { // ユーザ作成 $user = User::create($request->all()); AfterUserRegister::dispatch($user)->onQueue('high'); } }
ジョブの最大試行回数は以下のように指定します。
<?php namespace App\Jobs; class AfterUserRegister implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // 最大試行回数 public $tries = 5; }
ジョブの最大実効秒数は以下のように指定します。
<?php namespace App\Jobs; class AfterUserRegister implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // タイムアウト(秒) public $timeout = 90; }
最大試行回数以上ジョブの実行に失敗した場合、そのジョブは failed_jobs テーブルに保存されます。以下のコマンドでテーブルを作成します。
$ php artisan queue:failed-table $ php artisan migrate
失敗したジョブのクリーンアップはジョブクラスの failed メソッドで行います。なお、引数として失敗の原因となった Exception が渡されます。
<?php namespace App\Jobs; use App\User; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; class AfterUserRegister implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // 最大試行回数 public $tries = 5; protected $user; public function __construct(User $user) { // dispatchメソッドの引数でわたされる $this->user = $user; } public function handle() { // ジョブで処理する内容 } public function failed(Exception $exception) { // 失敗時のクリーンアップ } }
failed_jobs テーブルに保存されたジョブは以下のコマンドで確認できます。
$ php artisan queue:failed
失敗したジョブの再実行は以下のコマンドで行います。
// ID=5のジョブを再実行する場合 $ php artisan queue:retry 5 // 失敗したジョブを全て再実行する場合 $ php artisan queue:retry all
また、失敗したジョブの削除は以下のコマンドで行います。
// ID=5の失敗ジョブを削除する場合 $ php artisan queue:forget 5 // 全ての失敗ジョブを削除する場合 $ php artisan queue:flush
Laravelで非同期に処理を行う方法として、データベースを使う方法を紹介しました。