Breathnote
Amazon SQSを使ったLaravelメールキューを実装する

Amazon SQSを使ったLaravelメールキューを実装する

Laravelを使ったとあるWEBシステムに、メール送信機能を実装しました。このシステムでは、メールを一括送信する要件があったため、メール送信部分は非同期で実装しました。

Laravelでは、タスクをバックグラウンドで処理させる仕組みとして「キュー」が存在します。Laravelキューは、データベース、Amazon SQS、Redisといったドライバをサポートしていますが、今回はAmazon SQSの「FIFOキュー」を利用して実装しました。

この記事では、Amazon SQSを用いたメールキューの実装方法をまとめています。

動作環境

  • Laravel 7.0
  • aws/aws-sdk-phpを導入済み
  • メール送信機能を実装済み
  • Amazon SQSのFIFOキューを準備済み

実装

1. キューにメッセージを送るサービスを作成

SqsFifoQueue.php
<?php

namespace App\Services;

use Illuminate\Queue\SqsQueue;

class SqsFifoQueue extends SqsQueue
{
    public function pushRaw($payload, $queue = null, $options = [])
    {
        $response = $this->sqs->sendMessage([
            'QueueUrl' => $this->getQueue($queue),
            'MessageBody' => $payload,
            'MessageGroupId' => uniqid(),
            'MessageDeduplicationId' => uniqid(),
        ]);

        return $response->get('MessageId');
    }
}

2. キューに接続するサービスを作成

SqsFifoConnector.php
<?php

namespace App\Services;

use Aws\Sqs\SqsClient;
use Illuminate\Support\Arr;
use Illuminate\Queue\Connectors\SqsConnector;

class SqsFifoConnector extends SqsConnector
{
    public function connect($config)
    {
        $config = $this->getDefaultConfiguration($config);

        if ($config['key'] && $config['secret']) {
            $config['credentials'] = Arr::only($config, ['key', 'secret']);
        }

        return new SqsFifoQueue(new SqsClient($config), $config['queue']);
    }
}

3. キュー接続用のサービスプロバイダを作成

SqsFifoServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

use App\Services\SqsFifoConnector;

class SqsFifoServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->afterResolving('queue', function ($manager) {
            $manager->addConnector('sqsfifo', function () {
                return new SqsFifoConnector;
            });
        });
    }
}
config/app.php
return [
    'providers' => [
        App\Providers\SqsFifoServiceProvider::class,
    ],
];

4. キューの接続情報を追加

config/queue.php
return [
    'connections' => [
        'sqsfifo' => [
            'driver' => 'sqsfifo',
            'key' => env('AWS_QUEUE_ACCESS_KEY_ID'),
            'secret' => env('AWS_QUEUE_SECRET_ACCESS_KEY'),
            'queue' => env('SQS_QUEUE'),
            'region' => env('AWS_DEFAULT_REGION'),
        ],
    ],
];

5. 環境変数を追加

.env
# 変更
QUEUE_CONNECTION=sqsfifo

# 新規追加
AWS_QUEUE_ACCESS_KEY_ID=<[KEY_ID]>
AWS_QUEUE_SECRET_ACCESS_KEY=<[SECRET_KEY]>
SQS_QUEUE=<[END_POINT]>
AWS_DEFAULT_REGION=<[REGION]>

6. ジョブクラスの作成

SendSampleMail.php
<?php

namespace App\Jobs;

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

use App\Mail\SampleMail;

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

    public $tries = 3; // 処理失敗時の最大試行回数

    public $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function handle()
    {
        Mail::to('<[YOUR_ADDRESS]>')->send(new SampleMail(
            $this->data,
        ));
    }
}

7. メール送信部分を修正

実装済みのメール送信部分をdispatch()に置き換えます。

SampleController.php
<?php

namespace App\Http\Controllers;

class SampleController extends Controller
{
    public function index(Request $request)
    {
        // Mail::to('<[YOUR_ADDRESS]>')->send(new SampleMail($request));

        SendSampleMail::dispatch($request);
    }
}

8. キューワーカーを起動して動作確認

php artisan queue:workでキューワーカーを起動して、実際にメールを送信してみましょう。

Amazon SQSのモニタリング画面でレスポンスの返却が確認できればOKです。

所感

本番環境でキューサービスを利用する際は、Supervisorの導入が欠かせません。

こちらの記事でSupervisorの導入方法を説明しています。興味のある方はどうぞ。