株式会社WR

株式会社WR

WEB TOTAL CONSULTING

Laravelのキューを使ったメール送信の非同期化
ブログ一覧へ
技術ブログ

Laravelのキューを使ったメール送信の非同期化

お問い合わせフォームからメール送信する際、SMTP接続に時間がかかりユーザーを待たせることがあります。Laravelのキューで非同期化することでUXを改善できます。

なぜメール送信を非同期化するのか

Webアプリでメール送信をリクエスト処理の中で同期的に実行すると、以下の問題が発生します。

  • SMTPサーバーへの接続時間(数百ms〜数秒)がレスポンスタイムに加算される
  • 送信に失敗したとき、ユーザーへのレスポンスも失敗してしまう
  • 大量送信時にタイムアウトが発生しやすい

Laravelのキューシステムを使うことで、メール送信を「後でまとめて処理する」非同期処理に変換でき、ユーザーには即座にレスポンスを返せます。


キューの設定

.envでキュードライバーを設定

# 開発環境:同期実行(キューなしで即実行)
QUEUE_CONNECTION=sync

# 本番環境:データベースキュー
QUEUE_CONNECTION=database

# または Redis キュー(推奨)
QUEUE_CONNECTION=redis

データベースキューのマイグレーション

php artisan queue:table
php artisan migrate

Mailableクラスの作成

php artisan make:mail ContactReceived
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class ContactReceived extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(
        public readonly array $contactData,
    ) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: "【お問い合わせ受付】{$this->contactData['subject']}",
        );
    }

    public function content(): Content
    {
        return new Content(
            view: 'emails.contact-received',
            with: [
                'name'    => $this->contactData['name'],
                'email'   => $this->contactData['email'],
                'subject' => $this->contactData['subject'],
                'body'    => $this->contactData['body'],
            ],
        );
    }

    public function attachments(): array
    {
        return [];
    }
}

キューにジョブを投入する

Mail::send() の代わりに Mail::queue() を使うだけです。

// コントローラー
class ContactController extends Controller
{
    public function store(ContactRequest $request): RedirectResponse
    {
        $data = $request->validated();

        // DBに保存
        $contact = Contact::create($data);

        // キューに投入(即座に返る)
        Mail::to(config('mail.admin'))
            ->queue(new ContactReceived($data));

        // 送信者への自動返信もキューで
        Mail::to($data['email'])
            ->queue(new ContactAutoReply($data));

        return redirect()
            ->route('contact.complete')
            ->with('success', 'お問い合わせを受け付けました。確認メールをお送りします。');
    }
}

送信遅延

特定の時間に遅らせて送ることもできます。

// 5分後に送信
Mail::to($user->email)
    ->later(now()->addMinutes(5), new WelcomeMail($user));

// 翌朝9時に送信
Mail::to($user->email)
    ->later(now()->setHour(9)->addDay()->startOfHour(), new DailySummary($summary));

キューワーカーの起動

# ワーカーを起動(前景実行)
php artisan queue:work

# 失敗ジョブを再試行
php artisan queue:retry all

# キューの状態確認
php artisan queue:monitor

Supervisorで常時起動

本番環境ではSupervisorを使ってワーカーを常時起動します。

; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log

失敗ジョブのハンドリング

# 失敗ジョブテーブルを作成
php artisan queue:failed-table
php artisan migrate
// Mailableにfailedメソッドを追加
class ContactReceived extends Mailable
{
    public function failed(\Throwable $e): void
    {
        // Slackに通知するなど
        Log::error("メール送信失敗: {$this->contactData['email']}", [
            'error' => $e->getMessage(),
        ]);
    }
}

メールテスト

開発環境でのメール確認には、Mailpit が便利です。

# .env
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025

本番環境ではAmazon SES・SendGrid・Mailgunなどのサービスを利用することを推奨します。


まとめ

Laravelのキューを使ったメール送信の非同期化は、ユーザー体験の向上とシステムの堅牢性強化に直結します。Mail::queue() への変更はたった1単語ですが、その効果は大きいです。弊社では問い合わせ・予約確認・通知メールすべてにキューを活用しています。

LaravelのWebシステム開発についてはお気軽にご相談ください。

Category 技術ブログ

Related Posts

関連記事

開発・技術のご相談はお気軽に

お見積りは無料です。まずはお気軽にご相談ください。

お問い合わせ →