Laravel5.7でキューを使って重い処理を非同期で動かす

[Sponsored Links]

データの更新とリアルタイムで時間のかかる重い処理を実行していたために、画面がフリーズしてレスポンスがなくなってしまう事態が発生した。解決方法として、重い処理のみクーロン(定期実行)を提案したが、どうしてもリアルタイムでやりたいとのこと。

単純にその問題を解決するのはクーロンだ!と思っていたけれど、PHPのフレームワークとしてLaravelを使っているし、その要望を解決するもっと便利なものがあるかもと思って調べてみた。

そして、「Laravelのキュー」というものを知るのでした。調べていて、キューを知るきっかけになったのがこの記事。

4:「重い処理」をLaravelでどう実行するか?
Laravel使用時に困った点と対処方法

簡単に言うと、キューは、裏で時間を費やす処理を非同期で実行してくれる仕組みだそうです。さっそく使ってみます。

1.設定

テーブル準備

キューのジョブテーブルとキューが失敗した時用のテーブルを作成します。

#キュー用のテーブル
$ php artisan queue:table
$ php artisan queue:failed-table
$ php artisan migrate

.envファイル修正

QUEUE_CONNECTION=database

2.ジョブの作成

ジョブ作成コマンド実行。キューに投入するジョブを作成します。
ジョブが実行される処理の本体です。(キューはジョブを非同期で実行してくれる仕組み)

ジョブの作成

$ php artisan make:job UpdateProject

コマンドを実行すると、app/Jobsにクラスが作成されます。

<?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 UpdateProject implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//実行処理
}
}

public function handle()に実行したい処理を記載する。実行したい処理はサービスにまとめていたので今回は、下記のように記載する。下記はサンプル。

<?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;
use App\Services\Project\RegisterService;
class UpdateProject implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* ジョブがタイムアウトになるまでの秒数
*
* @var int
*/
public $timeout = 0;//0秒
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$register = new RegisterService();
$projects = $register->getProjects();
//対象をアップデート
if(isset($projects) && count($projects) > 0){
//UPDATE
foreach($projects as $project){
$register->update_trigger($project);
}
}
}
/**
* 失敗したジョブの処理
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception)
{
// 失敗の通知をユーザーへ送るなど…
}
}

実行のトリガー設置

ジョブがキューに投入されるタイミングを決めて、ジョブが実行されるトリガーを設置する。
今回は、データの更新された後に実行されるようにしたいので、Controller側に記述。

/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
.....
.....
.....
try {
.....
.....
//更新処理
UpdateProject::dispatch();
.....
.....
} catch (Exception $e) {
.....
.....
}
}

3.キューワーカの起動

状況に応じて、①または②の方法で。

①ローカル環境の場合

$ php artisan queue:work --sleep=3 --tries=3

キューを受け付ける状態にしておく。
そして、ジョブの最大実行回数3回、新しく処理するジョブが存在しない時は、3秒でスリープ。

動作確認

$ php artisan queue:work --sleep=3 --tries=3
[2019-01-31 17:17:52][1] Processing: App\Jobs\UpdateProject
[2019-01-31 17:17:53][1] Processed: App\Jobs\UpdateProject
[2019-01-31 17:19:11][2] Processing: App\Jobs\UpdateProject
[2019-01-31 17:19:11][2] Processed: App\Jobs\UpdateProject

②本番環境の場合

常時稼働するような本番環境では、キューワーカを永続的に実行させるために、Supervisor設定をします。

バックグランドでqueue:workプロセスを永続的に実行し続けるには、キューワーカが止まらずに実行し続けていることを確実にするため、Supervisorのようなプロセスモニタを利用する必要があります。

Laravel 5.7 キュー

Supervisor設定

supervisorをインストールします。

$ sudo easy_install supervisor
$ sudo yum install supervisor

設定をします。

$ sudo vim /etc/supervisord.conf

内容は下記のようにする。
commanduserstdout_logfile部分は、環境に合わせる)

[program:laravel-worker]
command=php /vagrant/www/artisan queue:work --sleep=3 --tries=3
process_name=%(program_name)s_%(process_num)02d
numprocs=8
priority=999
autostart=true
autorestart=true
startsecs=1
startretries=3
user=nginx
redirect_stderr=true
stdout_logfile=/var/log/worker.log
サービスの起動
$ sudo systemctl enable supervisord
$ sudo systemctl restart supervisord
動作確認

stdout_logfileで指定した場所にログがでるので確認してみる。

$ sudo tail -f /var/log/worker.log

失敗した場合は、テーブルfailed_jobsにも格納されるので、確認する

感想

調べてみると、やっぱり大抵のことは解決方法が存在するんだなと改めて実感しました。そして、自分の知識や経験の幅を広げてくれるのは、いつもクライアントさんの(無茶な)要望だなって思いました。感謝ですね。

自分の知ってる狭い知識でやりくりすることもあるけれど、やっぱりスキルはアップデートしていきたい!今後またこういった場面での選択肢を1つ増やすことができたのでした。

今後のTODOとしては、エラーのハンドリング処理は、別途考えていきたい。というところです。

参考サイト

■キュー
Laravel 5.7 キュー
Laravelでキューってみる
Laravelのキューイングを実装する
■Supervisor
Install and config supervisord on centos 7 to run Laravel queues permanently

  • このエントリーをはてなブックマークに追加
[Sponsored Links]