1. Home
  2. PHP
  3. Laravel
  4. Laravelのキュー投入によるジョブ処理を導入する

Laravelのキュー投入によるジョブ処理を導入する

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:Laravel,Queues,Job,Redis,Amazon SQS,Beanstalk
Laravelのキュー投入によるジョブ処理を導入する

Laravel のような PHP フレームワークでアプリケーションを構築すると、各々の役割に応じた処理別にソースコードを分離でき、可読性の高い開発が行えます。

しかしながら、その規模が大きくなってくるとさまざまな処理が増えてきます。その度にコントローラの仕事が増え、疎結合感が無くなってくるストレスもある中で、ある一つの処理が大きな処理時間を必要とした場合にはもうたまったものではありません。

しかもそれがユーザの待機時間にも影響を与えるとなるともうこれは…ただただ泣けます。

そんな時は処理をキューへ投入し、遅延処理を行いましょう。 Laravel のジョブキュー処理を実装すると非同期での処理を実現でき、ユーザへのレスポンスを劇的に早める事ができます。

メール送信やログ処理など、ユーザへのページ表示に関係なく、かつ重たい処理でユーザを待たせたくないですしね。

という事で今回は、laravel のキューサービスを利用し、ジョブ処理を実装します。

Contents

  1. 開発環境
  2. キューサービスについて
  3. キューサービスの設定
    1. ジョブクラス生成
    2. ジョブクラス実装
  4. コントローラ・ビュー・ルーティングの作成
  5. ジョブのディスパッチ
  6. 動作確認

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • MySQL 8.0/5.7
  • PHP 7.2/7.1

Laravel のバージョンについては、5.7/5.6/5.5 にて動作確認済みです。

また、laravel フレームワークのルートディレクトリを「laravel/」とします。

キューサービスについて

Laravel が対応しているキューサービスについては、以下になります。

  • sync
    • 外部環境やサービスを必要とせずにキュー&ジョブを実装できます。キューサービスのロジックをまず実装する場合におすすめですが、事実上、同期処理となり遅延処理にはなりません。
  • database
    • データベースを使ってキュー&ジョブ管理を行います。
  • Beanstalk
    • Beanstalkd という、オープンソースのジョブキュークライアントツールです。 これを利用するには、Beanstalkd の構築が必要です。
    • http://kr.github.io/beanstalkd/
  • Amazon SQS
    • AWS が提供しているキューサービスです。無料利用期間であれば無料枠がありますが、基本的には有料です。
    • https://aws.amazon.com/jp/sqs/
  • Redis
    • NoSQL データベースです。オープンソースですが、利用には Redis の構築が必要です。
    • https://redis.io

という事で、今回はジョブキューの導入が主な目的なので、はじめの一歩として sync を使ってサクッと実装してみましょう。

キューサービスの設定

どのキューサービスを使うかの設定は、laravel/config/queue.php で行います。開いて見てみます。

laravel/config/queue.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Queue Connection Name
    |--------------------------------------------------------------------------
    |
    | Laravel's queue API supports an assortment of back-ends via a single
    | API, giving you convenient access to each back-end using the same
    | syntax for every one. Here you may define a default connection.
    |
    */

    // 5.7から
    'default' => env('QUEUE_CONNECTION', 'sync'),
    
    // 5.6まで
    'default' => env('QUEUE_DRIVER', 'sync'),

    /*
    |--------------------------------------------------------------------------
    | Queue Connections
    |--------------------------------------------------------------------------
    |
    | Here you may configure the connection information for each server that
    | is used by your application. A default configuration has been added
    | for each back-end shipped with Laravel. You are free to add more.
    |
    | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
    |
    */

    'connections' => [

        'sync' => [
            'driver' => 'sync',
        ],

        'database' => [
            'driver' => 'database',
            'table' => 'jobs',
            'queue' => 'default',
            'retry_after' => 90,
        ],

        'beanstalkd' => [
            'driver' => 'beanstalkd',
            'host' => 'localhost',
            'queue' => 'default',
            'retry_after' => 90,
        ],

        'sqs' => [
            'driver' => 'sqs',
            'key' => env('SQS_KEY', 'your-public-key'),
            'secret' => env('SQS_SECRET', 'your-secret-key'),
            'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
            'queue' => env('SQS_QUEUE', 'your-queue-name'),
            'region' => env('SQS_REGION', 'us-east-1'),
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => env('REDIS_QUEUE', 'default'),
            'retry_after' => 90,
            'block_for' => null,
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Failed Queue Jobs
    |--------------------------------------------------------------------------
    |
    | These options configure the behavior of failed queue job logging so you
    | can control which database and table are used to store the jobs that
    | have failed. You may change them to any database / table you wish.
    |
    */

    'failed' => [
        'database' => env('DB_CONNECTION', 'mysql'),
        'table' => 'failed_jobs',
    ],

];

バージョンによってデフォルトキューの定数名が変更になっています。 5.7 の場合は QUEUE_CONNECTION で、5.6 までは QUEUE_DRIVER です。 default 利用するキューサービスを設定します。 記述が.env ファイルからの読み込みとなっている為、laravel/.env を確認してみます。

# Laravel 5.7
QUEUE_CONNECTION=sync

# Laravel 〜 5.6
QUEUE_DRIVER=sync

デフォルトで sync が設定されているため、今回は変更なしで OK です。 connections 各キューサービスの設定を行っています。 failed 失敗したジョブのロギングの動作を設定しています。 ここで書き込むデータベースとテーブルを指定できます。 ## ジョブクラスの作成

それではここから手を動かしていきます。

と、その前に、言い遅れましたが今回は「テキストファイルを作成し保存する処理」を実装して、それをジョブ化して処理していく流れにしますので、今回のジョブクラスは「StoreText 」とします。

ジョブクラス生成

ではまずはジョブクラスの作成です。 laravel ルートディレクトリへ移動し、以下の artisan コマンドを叩いてジョブクラスを生成します。

# laravel ルートディレクトリへ移動
cd /path/to/laravel

# artisan コマンドでジョブクラス生成する
php artisan make:job StoreText

# 実行結果
[demo@localhost laravel]$ php artisan make:job StoreText
Job created successfully.

laravel/app/Jobs ディレクトリが作成され、その中にジョブクラス StoreText.php が生成されます。

laravel
├─ app
│   ├─ Jobs
│       └─ StoreText.php

ジョブクラス実装

それでは生成したジョブクラスに処理を記述していきます。 まずはソースコードを見てみます。

laravel/app/Jobs/StoreText.php
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class StoreText implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

コンストラクタと handle() メソッドというシンプルなものになっています。

コンストラクタにデータセットを記述し、メイン処理は handle() メソッドに記述していきます。 以下のようになります。

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class StoreText implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $param;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($value)
    {
        $this->param = $value;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // テキストファイル作成
        $file = sprintf('%s/%s.txt', storage_path('texts'), date('Q-Ymd-His'));
        touch($file);
        // 書き込み
        $current = file_get_contents($file);
        $current .= $this->param;
        file_put_contents($file, $current);
    }
}

メンバ変数 param を定義し、コンストラクタでは、キュー投入時に渡される引数 value をメンバ変数 param へ格納しています。そして、handle() メソッドにテキストファイルを作成する処理を記述しています。

ジョブクラスはこれで実装完了です。

コントローラ・ビュー・ルーティングの作成

それではこのジョブクラスを動作させるために、コントローラとビューを簡単に実装してルーティングを行います。

ここは簡単に組むので、ダイジェストでソースだけ記します。

ルーティング laravel/routes/web.php
Route::get('sample/queues', 'SampleController@queues');
http://YOURDOMAIN/sample/queues へのアクセス時に SampleController の queues メソッドを実行します。 コントローラ laravel/app/Http/Controllers/SampleController.php
public function queues()
{
  return view('sample_queues');
}
単純に、sample_queues ビューを返します。 ビュー laravel/resources/views/sample_queues.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sample Queues</title>
</head>
<body>
<p>
  Queues test!!
</p>
</body>
</html>

ここには特別な処理は無く、「Queues test!!」と表示されるだけです。 最後に、生成したテキストファイルを格納するためのディレクトリ「texts 」を laravel/storage配下へ作成します。

laravel
├─ storage
│   └── texts

ジョブのディスパッチ

それでは仕上げです。作成したジョブをコントローラに組み込み、キューへ投入します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Jobs\StoreText;  // ジョブクラスをuseする

class SampleController extends Controller
{
  public function queues()
  {
    $text = str_random(1000);

    // ジョブをディスパッチする
    $this->dispatch(new StoreText($text));

    return view('sample_queues');
  }
}

ジョブクラスを use し、ジョブをディスパッチします。また、引数にはランダムに生成した 100 文字を渡しています。

動作確認

全ての実装が完了したので、ブラウザからアクセスしジョブが実行されているか確認してみます。

処理の流れとしては以下となります。

  1. http://YOURDOMAIN/sample/queues にアクセス
  2. コントローラ内でテキスト作成の処理をキューへ投入し、ビューを表示させる
  3. キューへ投入されたジョブが実行され、laravel/storage/texts にテキストファイルが生成される。
laravel
├─ storage
│   └── texts
│          └── Q-20190211-015407.txt

ジョブが実行され、テキストファイルが生成されている事が確認できました。

まとめ

以上でキュー&ジョブの導入は完了となります。

今回は sync キューを利用した為、実質的な遅延処理は行われていませんが、ここまで実装が出来たのなら、あとは別のキューサービスに切り替えればよいだけです。

という事で、次回はデータベースを使ってジョブキューを実装し、本格的に遅延処理を行いたいと思います。

next:Laravel のデータベースキュー投入とジョブ処理で非同期処理を実現する

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級