概要
キューとは
- 特定の順序でデータを保存および管理するために使用されるコンピューターサイエンスの基本的な概念です
- 先入れ先出し(FIFO)の原則に従っており、キューに最初に追加された要素が最初に削除されます
今回はデータベースのテーブルでキューの仕組みを再現できるデータベースキュードライバを使用します。
Laravel Sailで検証します
リポジトリはこちらです。
エイリアスで./vendor/bin/sail
をsail
に省略しています。
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
背景
業務でLaravelを使っていてqueueを触ったのでまとめます。
Queue(キュー)とは
特定の順序でデータを保存および管理するために使用されるコンピューターサイエンスの基本的な概念です。
先入れ先出し(FIFO)の原則に従っており、キューに最初に追加された要素が最初に削除されます。
Queue(キュー)の基本的な操作
- enqueue() - キューに要素を挿入する
- dequeue() - キューから要素を削除する
- peek() または front() - 削除せずに、キューの前ノードで利用可能なデータ要素を取得します
- rear() - この操作は、後端にある要素を削除せずに返します
- isFull() - キューが満杯であるかどうかを検証します
- isEmpty() - キューが空であるかどうかを確認します
- size() - キューのサイズを返します: この操作は、キューのサイズ、つまりキューが含む要素の総数を返します
LaravelのQueue(キュー)
Laravelではユーザの操作とそれに付随する重いタスク*を分離しユーザに早くレスポンスを返せます。
重いタスクはキューを利用して非同期に処理をします。
*例:アップロードされたCSVファイルの解析と保存、メール送信など
キューの設定ファイル
queueの設定ファイルは以下です。
<?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つです。
テーブルの仕組みを使用してキューを再現しています。
ジョブを保持するテーブルをつくる
defaultで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
<?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');
}
}
ジョブをキューにプッシュする
Route::get('/', function () {
Job::dispatch();
return view('welcome');
});
dispatchメソッドで
ジョブ自身のdispatch
メソッドを使ってJobをキューにプッシュすることができます。
ディスパッチメソッドに渡される引数はジョブのコンストラクタに渡されます。
jobsテーブルを見ると、queがdefault
のjobがプッシュされていることがわかります。
キューのワーカーを起動する
キューのワーカーを起動すると、キューにプッシュされたジョブを処理することができます。
sail artisan queue:work
キューにプッシュされたジョブが実行される時にJobクラス内のhandleメソッドが実行されます。
今回だと以下のようにログを出力する処理が実行されます。
/**
* Execute the job.
*/
public function handle(): void
{
//Jobが実行されたか確認
logger('job handled');
}
ログが出力されていることが確認できました
[2024-11-09 04:45:39] local.DEBUG: job handled
terminalを見るとジョブがRunning,Doneとなっていて処理中、実行完了したと表示されています。
テーブルからもレコードが削除されています。
キュー属性を指定してキューを区別して管理する
onQueue
メソッドでキューの属性を指定できます。
<?php
use App\Jobs\Job;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
Job::dispatch()->onQueue('job');
return view('welcome');
});
テーブルを見るとqueueカラムがjob
になっています。
この後、以下を実行してもこのジョブは実行されません。
sail artisan queue:work
このキューワーカーはキュー属性がdefault
のキューのジョブを実行します。
--queue=キューの属性
を指定します。
デフォルトは--queue=default
になっています。
キュー属性をjob
に指定します。
sail artisan queue:work --queue=job
これでキュー属性がjob
のキューがデキューされてからジョブが実行されます。
ジョブを複数のキューにプッシュすることでジョブの処理に優先順位をつけたり区別できます。
ジョブの実装中に詰まったこと
以下は自分がデバッグ中に詰まったことでdocにもありましたので共有します
- キューワーカーは起動した際のアプリケーションの状態をメモリに保存する為、起動中はコードの変更を反映できません
修正した際はキューワーカーを再起動する必要があります - この手間を省く方法として
queue:listen
コマンドを実行することもできます
しかし、queue:work
コマンドよりも大幅に効率が悪くなります
最後に
- queueの概要がつかめました
- 今度は実際にバックグラウンドで実行するような処理を実装してみます
- jobsテーブルにジョブがプッシュされて、ワーカーが検知してデキューされてからジョブが実行されるという一連の処理の流れを確認することができました
参考