Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
10
Help us understand the problem. What is going on with this article?
@ngmy

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

More than 1 year has passed since last update.

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>
10
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ngmy
Software engineer.

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
10
Help us understand the problem. What is going on with this article?