LoginSignup
7
14

More than 3 years have passed since last update.

laravel-async-queueでLaravelの非同期キューを小さく使い始める

Last updated at Posted at 2020-02-13

Laravelでキューを使うにはキューワーカーを立てる必要がありますが、キューワーカーのプロセス監視方法や、キューワーカーを使用しているアプリケーションのデプロイ方法を考える必要があり、結構面倒臭いです。
キューワーカーが不要な同期キュードライバもありますが、これだと非同期処理の旨味がありません。

laravel-async-queue

laravel-async-queueというパッケージが非同期キュードライバを提供しています。
この非同期キュードライバを使うと、ジョブをバックグラウンドプロセスで即時に実行できるようになり、キューワーカーを立てることなく非同期処理の旨味を得ることができます。

インストール方法

なお、この記事では下記のバージョンのソフトウェアを使用しています。

ソフトウェア バージョン
PHP 7.2.12
Laravel 5.5.44
laravel-async-queue v0.7.3

laravel-async-queueをComposerでインストールします。

composer require barryvdh/laravel-async-queue

config/app.phpにサービスプロバイダを追加します。

config/app.php
Barryvdh\Queue\AsyncServiceProvider::class,

config/queue.phpdefaultオプションをasyncに変更します。

config/queue.php
'default' => 'async', 

さらにconnectionsオプションに非同期キュードライバ用の設定を追加します。

config/queue.php
'async' => [
    'driver' => 'async',
    'table' => 'jobs',
    'queue' => 'default',
    'expire' => 60,
],

動作テスト

動作テストをしてみます。

テスト用のコントローラとイベントリスナーとイベントを用意します。

app/Http/Controllers/TestController.php
<?php

namespace App\Http\Controllers;

use App\Events\TestEvent;
use App\Http\Controllers\Controller;
use Log;

class TestController extends Controller
{
    public function test()
    {
        Log::debug('1');
        event(new TestEvent());
        Log::debug('3');
    }
}
app/Listeners/TestListener.php
<?php

namespace App\Listeners;

use App\Events\TestEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;

class TestListener implements ShouldQueue
{
    public function handle(TestEvent $event)
    {
        Log::debug('2')
    }
}
app/Events/TestEvent
<?php

namespace App\Events;

class TestEvent
{
}
app/Providers/EventServiceProvider.php
protected $listen = [
    'App\Events\TestEvent' => [
        'App\Listeners\TestListener',
    ],
];

コントローラのアクションを実行すると、下記のようなログが出力されます。

[2020-02-13 23:36:45] local.APP.DEBUG: 1 [] {"uid":"93eb38d","process_id":1017}
[2020-02-13 23:36:45] local.APP.DEBUG: 3 [] {"uid":"93eb38d","process_id":1017}
[2020-02-13 23:36:47] local.APP.DEBUG: 2 [] {"uid":"535fd9a","process_id":1046}

1→3→2となっており、非同期処理されていることがわかります。
プロセスIDとUIDもコントローラとイベントリスナーで別になっています。

ちなみに同期ドライバの場合です。

[2020-02-13 23:38:22] local.APP.DEBUG: 1 [] {"uid":"a9173e4","process_id":1020}
[2020-02-13 23:38:22] local.APP.DEBUG: 2 [] {"uid":"a9173e4","process_id":1020}
[2020-02-13 23:38:22] local.APP.DEBUG: 3 [] {"uid":"a9173e4","process_id":1020}

1→2→3となっており、同期処理です。
もちろんプロセスIDとUIDもコントローラとイベントリスナーで同じです。

失敗ジョブを扱えるようにする

非同期キュードライバは、キューワーカーを使っていないため、ジョブが1回失敗すると自動ではリトライされません。

また、ジョブ失敗ベントが発行されず、失敗したジョブはjobsテーブルに残ったままで、failed_jobsテーブルに入りません。

これだと運用がつらいので、ジョブ失敗をSlackに通知したりできるように、ジョブ失敗イベントが発行されるようにします。

イベントリスナーの$tiresプロパティを1に設定すると、ジョブ失敗ベントが発行されるようになります1

class TestListener implements ShouldQueue
{
    public $tires = 1;
    ...
}

トレイトにしておくと便利かもしれません。

trait AsyncQueueable
{
    public $tires = 1;
}
class TestListener implements ShouldQueue
{
    use AsyncQueueable;
    ...
}

これでジョブ失敗イベントが発生するようになります。
同時に、失敗ジョブがjobsテーブルから消えるようになります。

ただ、このままだと失敗ジョブがjobsテーブルにもfailed_jobsテーブルにも残らなくなってしまうので、失敗ジョブをfailed_jobsテーブルに入れる処理をジョブ失敗イベントのコールバックとして書くことにします。
これはAppServiceProviderbootメソッドに書くのがいいと思います。

app/Providers/AppServiceProvider
\Illuminate\Support\Facades\Queue::failing(function (\Illuminate\Queue\Events\JobFailed $event) {
    if ($event->job->getConnectionName() == 'async') {
        // 失敗ジョブをfaled_jobsテーブルに保存する
        $id = $this->app['queue.failer']->log(
            $event->connectionName,
            $event->job->getQueue(),
            $event->job->getRawBody(),
            $event->exception
        );
    }
    // Slackへの通知など
    ...
});

ちなみに、logメソッドはfailed_jobsテーブルのIDが返ってきます。
Slackに通知する時に使うと、後でジョブを手動でリトライする際に便利でいいと思います。

これで失敗ジョブがfailed_jobsテーブルに入るようになります。

リトライが必要な場合は、下記のArtisanコマンドを実行します。

artisan queue:retry <failed_jobsテーブルのID>

これでfailed_jobsテーブルの失敗ジョブがjobsテーブルに戻ります。

さらに下記のArtisanコマンドを実行するとジョブを実行できます。

artisan queue:async <jobsテーブルのID>
7
14
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
7
14