Job/Queue入門 ~Laravelの場合~

#前置き
LaravelのJob/Queueについて勉強する機会があったので、アウトプットします。
※プログラミング初心者なので、間違ってる点が多々あると思いますが、その際には是非ご指摘頂ければ…！

#なぜWebアプリで非同期が必要なのか？
①ユーザーエクスペリエンスの向上⇛非同期処理による「待ち時間」の削減
②システムの保守性の向上⇛Jobの切り分けによる疎結合なシステムの実現

この記事では特に①に焦点を当てて説明します。

#非同期/同期とは？
非同期：
>送信者のデータ送信タイミングと受信者のデータ受信タイミングを合わせずに通信を行う通信方式

同期：
>データ通信のリクエストを出してからレスポンスが来るまでほかの処理を行わずにレスポンスを待ち続ける

(http://www.atmarkit.co.jp/aig/07wcr/hidouki.html)

同期的な処理では、ユーザーからの「リクエスト」に対してWebアプリの「レスポンス」が逐一返さなくてはいけません。

それに対して非同期的な処理では、ユーザーからの「リクエスト」に対してWebアプリの「レスポンス」は逐一返さなくてもいいです。

具体例として、Webアプリによくある「ユーザー登録完了メール」を送信するケースで同期/非同期を見てみましょう。

#ユーザー登録に対して「ユーザー登録完了メール」を送信するケース
##同期の場合
ユーザー：「新規登録」ボタンをクリック
｜１秒
Webアプリ：メール送信処理開始
｜５秒
Webアプリ：メール送信処理完了
｜１秒
Webアプリ：ブラウザに「ユーザー登録が完了しました」と表示

同期処理の場合、ユーザーはメール送信処理の完了を待つ必要があります。
そのため、ユーザーにとって「遅いサイト」ということになります。

##非同期の場合
ユーザー：「新規登録」ボタンをクリック
｜１秒
Webアプリ：メール送信処理をQueueに登録
｜１秒
Webアプリ：ブラウザに「ユーザー登録が完了しました」と表示
｜１０秒
Queue：メール送信処理開始
｜5秒
Queue：メール送信処理完了

非同期処理の場合では、ユーザーはメール処理の完了を待つ必要がありません。
なぜなら、Webアプリがメール送信処理を別枠(Queue)に切り出しているからです。
そのため、実際にはメール送信処理に17秒かけているにも関わらず、ユーザーにとって「速いサイト」となります。

次は用語について簡単に説明します。

#用語
Job：何らかの処理
Queue：Jobを並ばせる「列」/保存先はDB/「どのDBにするか」によってドライバが変わる（SQS,Redis...）
Dispatch：JobをQueueに送ること
Worker：Queueの中のJobに対する処理するプロセス

用語は、処理と一緒に見てもらえれば、理解が速いと思います。
次に、LaravelでJob/Queueを実装したいと思います。

#Laravel実装
①Laravel新規プロジェクトを作成

```
composer create-project --prefer-dist laravel/laravel test "5.4.*"
```

②DBを準備する

```
touch database/databese.sqlite
```

※envファイル等の設定は他の記事を参考にしてください

③Queueを準備する

```
php artisan queue:table
php artisan migrate
```

これによって/database/migrationsに「(年月日時間)_reate_jobs_table.php」というテーブル定義ファイル作成及びデータベース反映が実行されます。

```php:queue.php
'default' => env('QUEUE_DRIVER', 'database')
```

また今回はQueueを自前のデータベースに準備したので、その旨を設定ファイルに記述します。
※QueueにSQS等の外部サービスを利用した場合は、この設定を変更します。

④Jobを定義

```
php artisan make:job SendReminderEmail
```

これによって、/app/Jobsにテンプレートファイルが作成されます。

```php:SendReminderEmail.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;
use Illuminate\Support\Facades\Log;

class SendReminderEmail 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()
    {
        Log::info('「登録が完了しました」というメールを送信');
    }
}
```

今回は単純化のために、実際のメール送信処理の代わりにログを出力させるように、Jobを定義します。
※このログは/storage/log/laravel.logに記述されます。

⑤JobのDispatchタイミングを定義

```
Route::get('/test', function () {
    $log = (new SendReminderEmail)->delay(10);
    dispatch($log);
    return 'ユーザー登録完了を通知するメールを送信しました。';
});
```

今回は/testにアクセスしたら「『10秒後にSendReminderEmailインスタンスを実行する』というJobをQueueに登録し、『ユーザー登録完了を通知するメールを送信しました。』という文を出力する」という風に実装しました。

⑥Workerの起動

```
php artisan queue:work
```

このWorkerの仕事は
1.QueueにJobがあるか問い合わせを行う
2.もしJobがあれば、そのJobを実行させる
というものです。
※問い合わせの頻度や、実行にかかる時間等をコマンドのオプションによって設定できます。


以上が、Laravelの実装になります。
DBを確認しつつ、/testにアクセスしたら、実際にJob/Queueがどのようになっているか理解できると思います。

#参考
https://readouble.com/laravel/5.4/ja/queues.html
http://tech.voyagegroup.com/archives/495474.html
http://www.techscore.com/blog/2015/12/05/active_job/
http://www.1x1.jp/blog/2014/08/laravel-queue-guide.html
https://kore1server.com/285/%E3%80%8C%E3%82%AD%E3%83%A5%E3%83%BC%E3%80%8D%E3%81%AF%E4%BD%95%E3%81%97%E3%81%ABLaravel%E3%81%B8%EF%BC%9F
https://laravel10.wordpress.com/2015/05/07/%E3%82%AD%E3%83%A5%E3%83%BC/