📘

JobとQueueを使用してLaravelで非同期処理をしよう!

2021/03/10に公開1

今回は、Job/Queueの概要説明と使用すべき理由についてお話します。

結論から言うと、
Job/Queueを知らなくてもシステムは作れます。小規模システムならほとんど問題はありません。
しかしJob/Queueを知っておいた方が良いですし、実際に使用した方が良いのは間違いないと思っています。理由は後述します。

それでは早速説明していきます!

Job/Queueとは?

Job/Queueは処理を非同期で実行する機能です。

Jobは実行する処理本体のことを指し、
Jobが呼ばれると、JobがQueueに登録されます。
そしてQueueの登録されているJobに先に登録されているものから順次実行されます。

例えばユーザー登録をする際に、このような処理が行われるとします。

【同期処理の場合の処理の流れ】

  1. フロントからのリクエストを受け取る(email, password)
  2. バリデーションを行う
  3. usersテーブルに登録する
  4. 登録したメールアドレス宛てにメールを送信する
  5. メール送信が完了したら処理終了!

例えばこの場合、「メール送信」を非同期で実行するとします。(Queue/Job)

すると、内部的な処理の流れはこのようになります

【非同期処理の場合の処理の流れ】

  1. フロントからのリクエストを受け取る(email, password)
  2. バリデーションを行う
  3. usersテーブルに登録する
  4. 登録したメールアドレス宛てにメールを送信する(Queueに登録)
  5. メール送信が完了したかどうかに関わらず処理終了!(...と同時にメール送信行っている最中です〜)

こちらのブログで図解されている方がいらっしゃってわかりやすかったです!
LaravelでQueueとJobを使ってみる基礎基礎メモ

Job/Queueを使うとなぜ良いのか?

上記の処理の違いを見てなにか気づくことはあるでしょうか?

後者のように非同期処理を行った方が、処理完了してフロント側に何かしら返すスピードが早くなります。サーバーサイドで「終わってないけど、終わった!」ってレスポンス返しているので、その分早くなるんですね。

メール送信の場合は正直そんなに変わらないですが、
より処理が大きい機能を扱う場合は、その分ユーザーを待たせることになってしまうので、非同期処理を検討してみてください。

早速使ってみる

早速試していきます。
手順は、Jobを作成して、Queueに突っ込むためのコードを一行書くだけです!

Jobを作成する

1.コマンドでJobを作成します

$ php artisan make:job SendUserRegistrationMail

すると、Jobディレクトリが作成されて、/app/Jobs/SendUserRegistrationMail.php Jobファイルができました!
もともとの中身はこんな感じです。
(laravel8系で試しています)

<?php

namespace App\Jobs;

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

class SendUserRegistrationMail 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 の中に処理を書いていきます。
今回はユーザー登録時のメール送信と仮定して、メール送信機能を書いていきます。
引数にユーザーをとるようにして、Notificationで通知を送ります。

<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

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

    protected $user;

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

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $this->user->sendRegistrationNotification();
    }
}

これでJobの作成が完了です!
なんて簡単なんでしょう!!

JobをQueueに突っ込むためのコードを一行書く

Jobが作成できたので、JobをQueueに突っ込む処理を書きます。

基本構文は Jobのクラス名::dispatch(引数) です。

ユーザー登録を行うControllerのメソッドに書きます

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class RegisterController extends Controller
{
    public function register(Request $request)
    {
	// バリデーションは行ったこととする(今回の趣旨とは異なるので)
	
	// リクエストを受け取る
	$attributes = $request->only(['email', 'password']);
	
	// ユーザーを作成する(作成時にpasswordをhash化していることにする。今回の趣旨とは異なるので)
	$user = User::create($attributes);
	
	// JobをQueueに突っ込む ← ここ!!!
	SendUserRegistrationMail::dispatch($user);
	
	return view('home');
    }
}

おわりに

結構簡単に非同期処理ができました。
今回はメール送信なので同期だろうが非同期だろうがそんなに変わらないと思いますが、
軽い処理であっても非同期でできる部分は非同期にして、少しでも早くユーザーにレスポンスを返して上げるのが良いかなと思います。

ご意見やご感想はコメント欄へお願いします!

Discussion

yuyu

おおおお、ぜひ使います。非常に勉強になります。
これをみて実際のレスポンスの比較検証をやりたいところですね・・