Laravel 5.1 キュー

イントロダクション

Laravelのキューサービスは、様々なキューバックエンドに対し共通のAPIを提供しています。キューによりメール送信のような時間を費やす処理を遅らせることが可能です。これによりアプリケーションのリクエストを徹底的に引き上げることができます。

設定

キューの設定ファイルはconfig/queue.phpです。このファイルにはフレームワークに含まれているそれぞれのドライバーへの接続設定が含まれています。それにはデータベース、BeanstalkdIronMQAmazon SQSRedis、同期(ローカル用途)ドライバーが含まれています。

nullキュードライバーはキューされたジョブが実行されないように、破棄するだけです。

ドライバー毎の必要要件

データベース

databaseキュードライバーを使用するには、ジョブを記録するためのデータベーステーブルが必要です。このテーブルを作成するマイグレーションはqueue:table Artisanコマンドにより生成できます。マイグレーションが生成されたら、migrateコマンドでデータベースをマイグレートしてください。

php artisan queue:table

php artisan migrate

他のドライバーに必要なパッケージ

以下の依存パッケージがリストしたキュードライバーを使用するために必要です。

ジョブクラスを書く

ジョブクラスの生成

キュー投入可能なアプリケーションの全ジョブは、デフォルトでapp/Jobsディレクトリーへ保存されます。新しいキュー投入ジョブはArtisan CLIで生成できます。

php artisan make:job SendReminderEmail --queued

このコマンドにより新しいクラスがapp/Jobsディレクトリーに生成され、そのクラスは同期的に実行する代わりにキューへジョブを投入することをLaravelに知らせる目印となる、Illuminate\Contracts\Queue\ShouldQueueインターフェイスを実装しています。

ジョブクラスの構造

ジョブクラスはとてもシンプルでキューでジョブが処理されるときに呼び出されるhandleメソッドだけで通常構成されています。手始めにこのジョブクラスを確認してみましょう。

<?php

namespace App\Jobs;

use App\User;
use App\Jobs\Job;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendReminderEmail extends Job implements SelfHandling, ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    protected $user;

    /**
     * 新しいジョブインスタンスの生成
     *
     * @param  User  $user
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * ジョブの実行
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function handle(Mailer $mailer)
    {
        $mailer->send('emails.reminder', ['user' => $this->user], function ($m) {
            //
        });

        $this->user->reminders()->create(...);
    }
}

この例中、キュージョブのコンテナーに直接Eloquentモデルが渡せることに注目してください。ジョブが使用しているSerializesModelsトレイトによりEloquentモデルは優雅にシリアライズされ、ジョブが処理される時にアンシリアライズされます。キュー投入されたジョブがコンテナでEloquentモデルを受け取ると、モデルの識別子のみシリアライズされています。ジョブが実際に処理される時、キューシステムは自動的にデータベースから完全なモデルインスタンスを再取得します。これらは全てアプリケーションの完全な透過性のためであり、Eloquentモデルインスタンスをシリアライズするときに発生する問題を防ぐことができます。

handleメソッドはキューによりジョブが処理されるときに呼びだされます。ジョブのhandleメソッドにタイプヒントにより依存を指定できることに注目してください。Laravelのサービスコンテナが自動的に依存を注入します。

上手く行かない場合

ジョブの実行時に例外が投げられると、再実行を試みれるように自動的にキューへ戻されます。ジョブはアプリケーションが許している最大回数まで続けて再実行されます。最大再実行回数はqueue:listenqueue:work Artisanコマンド実行時に--triesスイッチで指定します。キューリスナーの詳細はこの後に記述します。

ジョブを手動でリリースする

ジョブを自分で再実行したい場合、生成したジョブクラスでは含まれているInteractsWithQueueトレイトが提供するキュージョブのreleaseメソッドを使用してください。releaseメソッドは引数をひとつだけ取り、そのジョブを再実行可能にするまで待機する秒数を指定します。

public function handle(Mailer $mailer)
{
    if (condition) {
        $this->release(10);
    }
}

実行試行回数のチェック

前述の通り、ジョブの処理中に例外が起きた場合、そのジョブは自動的にキューに再登録されます。ジョブを実行しようとした試行回数をattemptsメソッドで調べることができます。

public function handle(Mailer $mailer)
{
    if ($this->attempts() > 3) {
        //
    }
}

ジョブのキュー投入

デフォルトLaravelコントローラーのapp/Http/Controllers/Controller.phpDispatchesJobsトレイトを使っています。このトレイトはdispatchメソッドのような、キューへジョブを便利に投入できるようにいくつかのメソッドを提供しています。

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * 指定ユーザーにリマインダーメールを送信する
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $this->dispatch(new SendReminderEmail($user));
    }
}

もちろんルートやコントローラーではないアプリケーションのどこからか、ジョブをディスパッチしたいこともあるでしょう。そのためDispatchesJobsトレイトはアプリケーションのどのクラスでも使えるようになっており、多くのディスパッチメソッドにアクセスできます。このトレイトを使用するサンプルクラスを見てください。

<?php

namespace App;

use Illuminate\Foundation\Bus\DispatchesJobs;

class ExampleClass
{
    use DispatchesJobs;
}

ジョブのキュー指定

さらに特定のキューにジョブを送ることもできます。

ジョブを異なったキューに送ることでキューするジョブを「カテゴリー分け」できます。さらに様々なキューにいくつのワーカーを割りつけるかによりプライオリティー付けできます。これはキュー設定ファイルで定義されている別々のキュー「接続」へジョブを送るという意味ではなく、一つの接続に対しキューを指定するだけで実現できます。キューを指定するにはジョブインスタンスのonQueueメソッドを使います。onQueueメソッドはApp\Jobs\Job基礎クラスに含まれる、Illuminate\Bus\Queueableトレイトにより提供しています。

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * 指定ユーザーにリマインダーメールを送信する
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $job = (new SendReminderEmail($user))->onQueue('emails');

        $this->dispatch($job);
    }
}

遅延ジョブ

投入したキュージョブの実行を遅らせたい場合もあるでしょう。たとえばサインアップの5分後に顧客へメールを送信するジョブをキューしたい場合などです。この場合、Illuminate\Bus\Queueableが提供しているdelayメソッドを使用して下さい。

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * 指定ユーザーにリマインダーメールを送信する
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $job = (new SendReminderEmail($user))->delay(60);

        $this->dispatch($job);
    }
}

この例ではワーカーにより実行可能にするまでキューの中のジョブを60秒遅延させると指定しています。

注目: Amazon SQSサービスには、遅延900秒(15分)という制限があります。

リクエストからのジョブディスパッチ

HTTPリクエストの変数をコマンドへマップしたいと考えるのは当然でしょう。それぞれのリクエストを手動で無理やりマップする代わりに、Laravelでは簡単に実現できるヘルパメソッドを用意しています。DispatchesJobsトレイトで使用できるdispatchFromメソッドを取り上げてみてみましょう。デフォルトでこのクラスはLaravelの基礎コントローラークラスに含まれています。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class CommerceController extends Controller
{
    /**
     * 指定された注文の処理
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function processOrder(Request $request, $id)
    {
        // リクエストの処理…

        $this->dispatchFrom('App\Jobs\ProcessOrder', $request);
    }
}

このメソッドは指定されたコマンドクラスのコンストラクターを調べ、それからHTTPリクエスト(もしくは他のArrayAccessオブジェクト)から変数を取り出し、必要なコマンドのコンストラクター引数を埋めます。ですからもしコマンドクラスがコンストラクターでproductId変数を取る場合、コマンドバスはHTTPリクエストからproductIdパラメーターを取り出そうとします。

dispatchFromメソッドはさらに第3引数に配列を指定できます。この配列はリクエストからは埋められないコンストラクター引数を埋めるために使用されます。

$this->dispatchFrom('App\Jobs\ProcessOrder', $request, [
    'taxPercentage' => 20,
]);

ジョブイベント

ジョブ完了イベント

Queue:afterメソッドにより、キューされたジョブの実行が成功した場合に起動されるコールバックを登録できます。このコールバックは追加のログを行ったり、続けて別のジョブをキューしたり、ダッシュボードに表示する情報を造化させたりするために最適でしょう。たとえばLaravelに含まれるAppServiceProviderにイベントのコールバックを追加してみましょう。

<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 全アプリケーションサービスの初期処理
     *
     * @return void
     */
    public function boot()
    {
        Queue::after(function ($connection, $job, $data) {
            //
        });
    }

    /**
     * サービスプロバイダーの登録
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

キューリスナーの実行

キューリスナーの起動

LaravelのArtisanは新しくキューに保存されたジョブを実行するコマンドを含んでいます。queue:listenコマンドを使いリスナーを実行できます。

php artisan queue:listen

リスナーに使用するキュー接続を指定することもできます。

php artisan queue:listen connection

このタスクを一度開始したら、手動で停止しない限り実行を続けることに注意してください。Supervisorのようなプロセスモニターを利用し、キューリスナーが確実に動作し続けるようにしてください。

キューの優先度

listenコマンドにキューのプライオリティーを設定するため、キュー接続をカンマ区切りで指定することもできます。

php artisan queue:listen --queue=high,low

この例でhighlowのジョブを実行する前にいつも処理されます。

ジョブのタイムアウトパラメータ指定

それぞれのジョブの実行時間を秒数で指定することもできます。

php artisan queue:listen --timeout=60

キュー休止時間の指定

さらに新しいジョブをポーリングする前に、待ち秒数を指定することもできます。

php artisan queue:listen --sleep=5

キューにジョブがない場合のみキューがスリープすることに注意して下さい。もしジョブが存在しているなら、キューはスリープせずに処理を続けます。

Supervisor設定

SupervisorはLinuxオペレーティングシステムの監視プロセスで、queue:listenqueue:workコマンドが落ちていれば自動的に再起動してくれます。UbuntuにSupervisorをインストールするには、次のコマンドで行います。

sudo apt-get install supervisor

Supervisorの設定ファイルは通常/etc/supervisor/conf.dディレクトリーに保存します。このディレクトリーの中には、Supervisorにどのようにプロセスを監視するのか指示する設定ファイルを好きなだけおくことができます。たとえば、laravel-worker.confファイルを作成し、queue:workプロセスを起動、監視させてみましょう。

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --daemon
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log

この例のnumprocsディレクティブはSupervisorに全部で8つのqueue:workプロセスを実行・監視し、落ちていれば自動的に再起動するように指示しています。もちろんcommandディレクティブのqueue:work sqsの部分を変更し、選択したドライバーに合わせてください。

設定ファイルができたら、Supervisorの設定を更新し起動するために以下のコマンドを実行してください。

sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*

Supervisorの設定と使用法の詳細は、Supervisorのドキュメントを読んでください。もしくは便利なWebインターフェイスからSupervisorを設定、管理できるLaravel Forgeを使うこともできます。

デーモンキューリスナー

queue:workはフレームワークを再起動せずに連続してジョブを処理し続けるようにキューワーカーを強制するために--daemonオプションも備えています。これによりqueue:listenと比較すると、CPU使用率を大幅に引き下げることができます。

キュー・ワーカーをデーモンモードで開始するためには、--daemonフラッグを使用します。

php artisan queue:work connection --daemon

php artisan queue:work connection --daemon --sleep=3

php artisan queue:work connection --daemon --sleep=3 --tries=3

ご覧の通りqueue:workコマンドはqueue:listenで使用できるものと、ほぼ同じオプションをサポートしています。php artisan help queue:workコマンドで全オプションを表示できます。

デーモンキューワーカー使用時のコーディング留意点

デーモンキューワーカーは各ジョブを処理する前にフレームワークを再起動しません。そのため多くのリソースを使用するジョブを完了する前に、それらを開放するように気をつけてください。たとえばGDライブラリーを使用し画像処理を行う場合、完了したらimagedestroyでメモリを開放する必要があるでしょう。

同様に、長時間動作するデーモンが使用し続ければデータベース接続は切断されるでしょう。新しい接続を行うためにDB::reconnectメソッドを使う必要が起きるでしょう。

デーモンキューリスナーのデプロイ

デーモンキューワーカーは長時間起動するプロセスですので、リスタートしなければコードの変更が反映されません。そのためデーモンキューワーカーを使用しているアプリケーションをデプロイする簡単な方法は、スクリプトをデプロイしている間にワーカーをリスタートすることです。デプロイスクリプトに以下のコマンドを含めることで、全スクリプトを穏やかにリスタートできます。

php artisan queue:restart

このコマンドは存在しているジョブが失われないように、現在のジョブの処理が終了した後に全キューワーカーを再起動するように穏やかに指示します。

注意: このコマンドは再起動のスケジュールするため、キャッシュシステムを利用しています。デフォルト状態ではAPCuはCLIコマンドのために動作しません。APCuを使用する場合はapc.enable_cli=1をAPCu設定に追加してください。

失敗したジョブの処理

物事は計画通りうまく行かない場合もありますので、キュージョブが失敗することも想定できます。でも心配ありません。最高な人たちも失敗はするものです! Laravelは指定した回数ジョブを再実行する便利な方法を用意しています。この回数実行してもうまく行かない場合は、faild_jobsテーブルに登録されます。失敗したジョブのテーブル名はconfig/queue.php設定ファイルで指定できます。

faild_jobsテーブルのマイグレーションを生成するにはqueue:faild-tableコマンドを実行して下さい。

php artisan queue:failed-table

キューリスナーを実行時にqueue:listenコマンドに--triesスイッチを使い、ジョブの最大試行回数を指定することもできます。

php artisan queue:listen connection-name --tries=3

ジョブ失敗イベント

キュージョブが失敗した時に呼び出されるイベントのリスナーを登録したい場合は、Queue::failingメソッドを使って下さい。このイベントはメールかHipChatであなたのチームに通知するのに便利でしょう。例としてLaravelに含まれているAppServiceProviderにこのイベンのコールバックを追加してみましょう。

<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        Queue::failing(function ($connection, $job, $data) {
            // ジョブが失敗したことをチームへ通知…
        });
    }

    /**
     * サービスプロバイダー登録
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

ジョブクラスのfailedメソッド

更に細かくコントロールするために、failedメソッドをキュージョブクラスへ直接定義することもできます。ジョブが失敗した時に特定のジョブアクションを実行できるようにできます。

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendReminderEmail extends Job implements SelfHandling, ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    /**
     * ジョブの実行
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function handle(Mailer $mailer)
    {
        //
    }

    /**
     * 失敗したジョブの処理
     *
     * @return void
     */
    public function failed()
    {
        // ジョブが失敗した時に呼び出される…
    }
}

失敗したジョブの再実行

faild_jobsデータベーステーブルに挿入された、失敗したジョブを全部確認したい場合はqueue:failed Arisanコマンドを利用します。

php artisan queue:failed

queue:failedコマンドにジョブIDを指定すれば、接続、キュー、失敗した時間がリストされます。ジョブIDは失敗したジョブを再実行する場合にも使用します。たとえばIDが5の失敗したジョブを再実行するには、以下のコマンドを実行します。

php artisan queue:retry 5

失敗したジョブをすべて再試行するには、queue:retryでIDの代わりにallを指定します。

php artisan queue:retry all

失敗したジョブを削除するにはqueue:forgetコマンドを使います。

php artisan queue:forget 5

失敗したジョブを全部消去するにはqueue:flushコマンドを使用します。

php artisan queue:flush

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる