1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravelのqueue(キュー)について理解する

Last updated at Posted at 2024-11-09

概要

キューとは

  • 特定の順序でデータを保存および管理するために使用されるコンピューターサイエンスの基本的な概念です
  • 先入れ先出し(FIFO)の原則に従っており、キューに最初に追加された要素が最初に削除されます

今回はデータベースのテーブルでキューの仕組みを再現できるデータベースキュードライバを使用します。

Laravel Sailで検証します
リポジトリはこちらです。
エイリアスで./vendor/bin/sailsailに省略しています。

~/.zshrc
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'

背景

業務でLaravelを使っていてqueueを触ったのでまとめます。

Queue(キュー)とは

特定の順序でデータを保存および管理するために使用されるコンピューターサイエンスの基本的な概念です。

先入れ先出し(FIFO)の原則に従っており、キューに最初に追加された要素が最初に削除されます。

image.png

Queue(キュー)の基本的な操作

  • enqueue() - キューに要素を挿入する
  • dequeue() - キューから要素を削除する
  • peek() または front() - 削除せずに、キューの前ノードで利用可能なデータ要素を取得します
  • rear() - この操作は、後端にある要素を削除せずに返します
  • isFull() - キューが満杯であるかどうかを検証します
  • isEmpty() - キューが空であるかどうかを確認します
  • size() - キューのサイズを返します: この操作は、キューのサイズ、つまりキューが含む要素の総数を返します

LaravelのQueue(キュー)

Laravelではユーザの操作とそれに付随する重いタスク*を分離しユーザに早くレスポンスを返せます。
重いタスクはキューを利用して非同期に処理をします。
*例:アップロードされたCSVファイルの解析と保存、メール送信など

キューの設定ファイル

queueの設定ファイルは以下です。

queue-app/config/queue.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Queue Connection Name
    |--------------------------------------------------------------------------
    |
    | Laravel's queue supports a variety of backends via a single, unified
    | API, giving you convenient access to each backend using identical
    | syntax for each. The default queue connection is defined below.
    |
    */

    'default' => env('QUEUE_CONNECTION', 'database'),

    /*
    |--------------------------------------------------------------------------
    | Queue Connections
    |--------------------------------------------------------------------------
    |
    | Here you may configure the connection options for every queue backend
    | used by your application. An example configuration is provided for
    | each backend supported by Laravel. You're also free to add more.
    |
    | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
    |
    */

    'connections' => [

        'sync' => [
            'driver' => 'sync',
        ],

        'database' => [
            'driver' => 'database',
            'connection' => env('DB_QUEUE_CONNECTION'),
            'table' => env('DB_QUEUE_TABLE', 'jobs'),
            'queue' => env('DB_QUEUE', 'default'),
            'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
            'after_commit' => false,
        ],

ここからわかることは以下です。

  • 環境変数QUEUE_CONNECTIONが設定されていない場合、デフォルトでdatabaseドライバを使用するようになっています
  • キューのジョブはjobsテーブルに保存されます(DB_QUEUE_TABLE環境変数で変更可能)
  • デフォルトのキュー名はdefaultです(DB_QUEUE環境変数で変更可能)
  • ジョブの再試行は90秒後に行われます(DB_QUEUE_RETRY_AFTER環境変数で変更可能)

データベースキュードライバ(Database Queue Driver)

前の章で述べたdatabaseドライバのことです。
Laravelのキューシステムで使用できるドライバの1つです。
テーブルの仕組みを使用してキューを再現しています。

今回は最終的にこのような仕組みを作ります。
image.png

ジョブを保持するテーブルをつくる

defaultで0001_01_01_000002_create_jobs_table.phpというマイグレーションファイルが作られています。

database/migrations/0001_01_01_000002_create_jobs_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('jobs', function (Blueprint $table) {
            $table->id();
            $table->string('queue')->index();
            $table->longText('payload');
            $table->unsignedTinyInteger('attempts');
            $table->unsignedInteger('reserved_at')->nullable();
            $table->unsignedInteger('available_at');
            $table->unsignedInteger('created_at');
        });

        Schema::create('job_batches', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->string('name');
            $table->integer('total_jobs');
            $table->integer('pending_jobs');
            $table->integer('failed_jobs');
            $table->longText('failed_job_ids');
            $table->mediumText('options')->nullable();
            $table->integer('cancelled_at')->nullable();
            $table->integer('created_at');
            $table->integer('finished_at')->nullable();
        });

        Schema::create('failed_jobs', function (Blueprint $table) {
            $table->id();
            $table->string('uuid')->unique();
            $table->text('connection');
            $table->text('queue');
            $table->longText('payload');
            $table->longText('exception');
            $table->timestamp('failed_at')->useCurrent();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('jobs');
        Schema::dropIfExists('job_batches');
        Schema::dropIfExists('failed_jobs');
    }
};

これがない場合は、以下でmigrationファイルを作成できます。

sail artisan make:queue-table

Jobクラスを作る

Laravelではキューで実行されるタスクの単位をJobと言います。

sail artisan make:job Job
queue-app/app/Jobs/Job.php
<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class Job implements ShouldQueue
{
    use Queueable;

    /**
     * Create a new job instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        //Jobが実行されたか確認
        logger('job handled');
    }
}

ジョブをキューにプッシュする

queue-app/routes/web.php
Route::get('/', function () {
    Job::dispatch();
    return view('welcome');
});

dispatchメソッドで
ジョブ自身のdispatchメソッドを使ってJobをキューにプッシュすることができます。
ディスパッチメソッドに渡される引数はジョブのコンストラクタに渡されます。

jobsテーブルを見ると、queがdefaultのjobがプッシュされていることがわかります。

image.png

キューのワーカーを起動する

キューのワーカーを起動すると、キューにプッシュされたジョブを処理することができます。

sail artisan queue:work

キューにプッシュされたジョブが実行される時にJobクラス内のhandleメソッドが実行されます。
今回だと以下のようにログを出力する処理が実行されます。

queue-app/app/Jobs/Job.php
/**
 * Execute the job.
*/
    public function handle(): void
    {
        //Jobが実行されたか確認
        logger('job handled');
    }

ログが出力されていることが確認できました

queue-app/storage/logs/laravel.log
[2024-11-09 04:45:39] local.DEBUG: job handled  

terminalを見るとジョブがRunning,Doneとなっていて処理中、実行完了したと表示されています。

image.png

テーブルからもレコードが削除されています。

image.png

キュー属性を指定してキューを区別して管理する

onQueueメソッドでキューの属性を指定できます。

queue-app/routes/web.php
<?php
use App\Jobs\Job;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    Job::dispatch()->onQueue('job');
    return view('welcome');
});

テーブルを見るとqueueカラムがjobになっています。

image.png

この後、以下を実行してもこのジョブは実行されません。

sail artisan queue:work

このキューワーカーはキュー属性がdefaultのキューのジョブを実行します。

--queue=キューの属性を指定します。
デフォルトは--queue=defaultになっています。

キュー属性をjobに指定します。

sail artisan queue:work --queue=job

image.png

これでキュー属性がjobのキューがデキューされてからジョブが実行されます。

image.png

ジョブを複数のキューにプッシュすることでジョブの処理に優先順位をつけたり区別できます。

image.png

image.png

ジョブの実装中に詰まったこと

以下は自分がデバッグ中に詰まったことでdocにもありましたので共有します

  • キューワーカーは起動した際のアプリケーションの状態をメモリに保存する為、起動中はコードの変更を反映できません
    修正した際はキューワーカーを再起動する必要があります
  • この手間を省く方法としてqueue:listenコマンドを実行することもできます
    しかし、queue:workコマンドよりも大幅に効率が悪くなります

最後に

  • queueの概要がつかめました
  • 今度は実際にバックグラウンドで実行するような処理を実装してみます
  • jobsテーブルにジョブがプッシュされて、ワーカーが検知してデキューされてからジョブが実行されるという一連の処理の流れを確認することができました

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?