お問い合わせ

ブログ

これまでに経験してきたプロジェクトで気になる技術の情報を紹介していきます。

Laravel Queues

okuda Okuda 3 years

Queues

時間のかかるタスクをバックグラウンドで実行する機能
実行したい処理(ジョブ)をリスト(キュー)に入れて置き、後で実行(ワーカ)するもの

コネクション

コネクションは「sync」、「database」、「beanstalkd」、「sqs」、「redis」などが使え、 config/queue.phpconnections に設定

「sync」はデフォルトで .env に設定されており同期処理される

databaseを使って実装

databaseコネクションの選択

.envQUEUE_CONNECTION を「sync」から「database」に変更

QUEUE_CONNECTION=database

テーブルの準備

artisanでキューが保存されるテーブルのマイグレーションファイルを作成

php artisan queue:table

2021_xx_xx_xxxxxx_create_jobs_table.php が作成される

migrateする

php artisan migrate

以下の2つのテーブルが作成されていることを確認

  • jobs
  • failed_jobs

ジョブの作成

php artisan make:job SampleJob

app\Jobs\SampleJob.php

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
// 追加
use Illuminate\Support\Facades\Log;
use App\Models\User;

class SampleJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    // パラメータを使う場合は宣言
    public $user;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    // ユーザを使うのでコンストラクタの引数に「User $user」を追加
    public function __construct(User $user)
    {
        $this->user = $user; // 追加
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $date = now()->format('Y-n-j H:i:s');

        // ログファイルにユーザと時間を表示
        Log::info("{$this->user->id}: {$this->user->name} [{$date}]");
    }
}

テストコントローラを作成

ジョブを登録するには dispatch メソッドを使用する

php artisan make:controller TestController

app\Http\Controllers\TestController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Jobs\SampleJob;
use App\Models\User;

class TestController extends Controller
{
    public function index()
    {
        // get user
        $user = User::find(1);

        // set job
        // dispatchでキューに登録
        SampleJob::dispatch($user);

        return 'aet job!';
    }
}

テストルートを作成

Route::get('/test', [TestController::class, 'index']);

実行

http://localhost/test にブラウザでアクセスする

これでjobsテーブルにジョブが追加される

id queue payload attempts reserved_at available_at created_at
1 default {"uuid":"62021aef-afa7-4842-9ad7-7fd... 0 nill 1630053240 1630053240

キューを実行

artisanでキューを実行する

php artisan queue:work

# output 
[2021-08-27 08:40:30][1] Processing: App\Jobs\SampleJob
[2021-08-27 08:40:30][1] Processed:  App\Jobs\SampleJob

storage\logs\laravel.log

[2021-08-27 08:40:30] local.INFO: 1: user1 [2021-8-27 08:40:30]  

redisに変更

predisモジュールを追加

モジュールを追加

composer require predis/predis

redisコネクションの選択

.env

QUEUE_CONNECTION=redis

redisで実行

php artisan queue:work

プロパティ

app\Jobs\SampleJob.php

    /**
     * ジョブの一意のロックが解放されるまでの秒数
     *
     * @var int
     */
    public $uniqueFor = 3600;

    /**
     * タイムアウトになる前にジョブを実行できる秒数
     *
     * @var int
     */
    public $timeout = 120;

    /**
     * ジョブを試行する回数。
     *
     * @var int
     */
    public $tries = 5;

    /**
     * 失敗する前に許可する未処理の例外の最大数
     *
     * @var int
     */
    public $maxExceptions = 3;

    /**
     * ジョブがタイムアウトで失敗したとマークするかを指定
     *
     * @var bool
     */
    public $failOnTimeout = true;

メソッド

キューの待機時間

ジョブがキューに入ってから設定時間待機してから実行させる

// 1分後を取得
$delay = now()->addMinutes(1);

// キューにディスパッチ、1分後に実行
SampleJob::dispatch($user)->delay($delay);

別のConnectionを利用

// コネクションredisを選択
SampleJob::dispatch($user)->onConnection('redis');

別のキューを利用

// コネクションredisを選択
SampleJob::dispatch($user)->onQueue('myQueue');
# defaultだけ実行
php artisan queue:work
# myQueueだけ実行
php artisan queue:work --queue=myQueue
# 複数実行、順に優先度になる
php artisan queue:work --queue=default,myQueue

コマンド

実行コマンド

  • queue:work: コードの変更があれば一度停止して、再度起動する必要がある、負荷小、本番用
  • queue:listen: 一度起動をすればコードの修正を行なっても再起動する必要がない、負荷大、開発用
  • queue:work --delay [second]: ジョブが失敗すると指定秒後に再実行するよう--dilayオプションを付けて実行
  • queue:work --tries [times]: リトライする回数を--triesで指定して実行、1以上
  • queue:work --timeout [second]: タイムアウト時間(秒)を--timeoutで指定して実行、config\queue.phpで指定するretry_afterより小さくなくてはいけない
  • queue:work --queue=[queue]: キューを指定して実行
  • queue:work [connection]: コネクションを指定して実行
  • queue:work [connection] --queue=[queue]: コネクションとキューを指定して実行
  • queue:restart: 再起動

実行終了コマンド

  • queue:work --once: キューから1つのジョブのみ実行して終了
  • queue:work --max-jobs=[times]: 処理するジョブの数を指定して実行して終了
  • queue:work --stop-when-empty: すべてのジョブを処理してから終了
  • queue:work --max-time=[second]: 指定する秒数の間ジョブを処理してから終了

キューからのジョブクリア

  • queue:clear: キューをすべてクリア
  • queue:clear [connection] --queue=[queue]: コネクションとキューを指定してクリア

失敗コマンド

  • queue:failed [uuid]: コマンドを実行すると失敗したジョブの情報が登録されていることが確認できる
  • queue:retry [uuid]: 失敗したジョブのuuidを指定して再実行
  • queue:retry [id]: 失敗したジョブのidを指定して再実行
  • queue:retry [id id id]: 失敗したジョブのidを指定して再実行
  • queue:retry --range=[id-id]: 失敗したジョブのidをレンジで指定して再実行
  • queue:forget [uuid]: 失敗したジョブのuuidを指定して削除
  • queue:forget [id]: 失敗したジョブのidを指定して削除
  • queue:flush: 失敗したジョブの一括削除
  • queue:retry --queue=[queue]: 失敗したジョブのキューを指定して再実行
  • queue:retry all: 失敗したジョブをすべて再実行

テーブルコマンド

  • queue:table: jobsテーブル作成
  • queue:failed-table: failed_jobsテーブル作成

config

config/queue.phpconnections に設定されているretry_afterは キューが実行されてからリトライするまでの時間(秒)
redisのみ対応
1つのジョブを実行してから次のジョブまでの待機時間

イメージとしは以下のような感じ

while(true){

    $job = getJob();

    // job process

    sleep(5);
}

ジョブ失敗処理

failedメソッドを作成して

    /**
     * ジョブの実行
     */
    public function handle(AudioProcessor $processor)
    {
        // Process uploaded podcast...
    }

    /**
     * ジョブの失敗を処理
     *
     * @param  \Throwable  $exception
     * @return void
     */
    public function failed(Throwable $exception)
    {
        // ロールバックの処理を追加したり、
        // ユーザへ処理の失敗を通知したりできる
    }
Laravel Queues 2021-11-18 10:28:06

コメントはありません。

4684

お気軽に
お問い合わせください。

お問い合わせ
gomibako@aska-ltd.jp