Laravel 5.2 キュー

イントロダクション

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

設定

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

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

ドライバ毎の必要要件

データベース

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

php artisan queue:table

php artisan migrate

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

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

ジョブクラスを書く

ジョブクラスの生成

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

php artisan make:job SendReminderEmail

このコマンドにより新しいクラスが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\Queue\ShouldQueue;

class SendReminderEmail extends Job implements 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トレイト

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

<?php

namespace App;

use Illuminate\Foundation\Bus\DispatchesJobs;

class ExampleClass
{
    use DispatchesJobs;
}

dispatch関数

もしくは、dispatchグローバル関数を使うこともできます。

Route::get('/job', function () {
    dispatch(new App\Jobs\PerformTask);

    return 'Done!';
});

ジョブのキュー指定

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

ジョブを異なったキューに送ることでキューするジョブを「カテゴリー分け」できます。さらに様々なキューにいくつのワーカーを割りつけるかによりプライオリティー付けできます。これはキュー設定ファイルで定義されている別々のキュー「接続」へジョブを送るという意味ではなく、一つの接続に対しキューを指定するだけで実現できます。キューを指定するにはジョブインスタンスの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);
    }
}

注意: DispatchesJobsトレイトは、デフォルトキュー接続のキューへジョブを投入します。

ジョブのキュー接続指定

複数のキュー接続を取り扱う場合、ジョブへ投入する接続を指定する必要が起きます。接続の指定には、ジョブインスタンスのonConnectionメソッドを使います。onConnectionメソッドはIlluminate\Bus\Queueableトレイトにより提供されており、App\Jobs\Jobベースクラスで取り込まれています。

<?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))->onConnection('alternate');

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

もちろん、ジョブのキューと接続を指定するために、onConnectiononQueueメソッドをチェーンすることもできます。

public function sendReminderEmail(Request $request, $id)
{
    $user = User::findOrFail($id);

    $job = (new SendReminderEmail($user))
                    ->onConnection('alternate')
                    ->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 * 5);

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

この例では、ワーカーで実行可能になるまで、キューの中のジョブを5分間遅延させると指定しています。

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

ジョブイベント

ジョブのライフサイクルイベント

Queue::beforeQueue::afterメソッドにより、キュージョブが実行開始した時と、成功裏に完了した時点で実行されるコールバックを登録することができます。コールバックにより、追加のログを残したり、続いて実行すべきジョブをキューへ投入したり、ダッシュボード上の回数を増加させたりする、タイミングが提供されます。例として、Laravelに含まれているAppServiceProviderで、このイベントにコールバックを指定してみます。

<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobProcessed;

class AppServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        Queue::after(function (JobProcessed $event) {
            // $event->connectionName
            // $event->job
            // $event->data
        });
    }

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

キューリスナの実行

キューリスナの起動

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

php artisan queue:listen

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

php artisan queue:listen connection-name

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

キューの優先度

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

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

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

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

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

php artisan queue:listen --timeout=60

キュー休止時間の指定

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

php artisan queue:listen --sleep=5

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

キュー上の最初のジョブの実行

キューの最初のジョブだけを実行するには、queue:workコマンドを使います。

php artisan queue:work

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-name --daemon

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

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

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

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

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

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

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

php artisan queue:restart

このコマンドは存在しているジョブが失われないように、現在のジョブの処理が終了した後に、全キューワーカーへ穏やかに「終了する(die)」よう指示します。キューワーカーはqueue:restartコマンドが実行されると、終了することを思い出してください。ですから、キュージョブを自動的に再起動する、Supervisorのようなプロセスマネージャーを実行すべきでしょう。

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

失敗したジョブの処理

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

failed_jobsテーブルのマイグレーションを生成するにはqueue:failed-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\Queue\Events\JobFailed;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        Queue::failing(function (JobFailed $event) {
            // $event->connectionName
            // $event->job
            // $event->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\Queue\ShouldQueue;

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

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

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

失敗したジョブの再実行

failed_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)へ移動

その他

?

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