Laravel で Redis と Supervisor を使ってメール送信を非同期にする


やりたいこと

メール送信処理を非同期で実行できるようにしたい。
つまりphp artisan queue:workがバックグラウンドで実行されている状態を目指す。

そのためにはキャッシュサーバーと、上記コマンドがバックグラウンドで実行できる環境が必要。
なので、Redis(キャッシュサーバー)と Supervisor(上述コマンドがバックグラウンドで実行できる環境)を導入する。

※Queue サービスが SQS とかになってもこの手順は変わらないはず。

環境

  • CentOS 7
  • PHP 7
  • Laravel 5.3

Redis インストール

インストール

sudo yum --enablerepo=remi,remi-test,epel install redis

起動

sudo systemctl start redis

起動確認

redis-cli ping

PONG

と表示されればOK

Redis クライアントライブラリインストール

設定周り変更

.env
QUEUE_DRIVER=redis
config/queue.php
'default' => env('QUEUE_DRIVER', 'redis'),
composer.json
"predis/predis": "~1.0"

Jobを作成する

php artisan make:job SampleJob

->Job created successfully.

確認用に適当な出力を書く

app/Jobs/SampleJob.php
public function handle()
{
    echo 123;
}

Queue に登録する

SampleController.php
  dispatch(new \App\Jobs\SampleJob());

Queue リスナー起動

php artisan queue:listen

123

と表示されればOK

Supervisor インストール

supervisorsupervisordが紛らわしいので注意。

インストール

sudo yum install supervisor

バージョン確認

rpm -qa | grep supervisor

supervisord 起動

sudo systemctl start supervisord.service

起動確認

sudo systemctl status supervisord.service

自動起動にする

sudo systemctl enable supervisord.service

設定ファイル編集

必要最低限の部分だけ編集する。
そもそもこの場所に無いかもしれないので注意。

/etc/supervisord.conf
[inet_http_server]  ; inet (TCP) server disabled by default
port=0.0.0.0:9001   ; (ip_address:port specifier, *:port for all iface)
username=user       ; (default is no username (open server))
password=123        ; (default is no password (open server))

[program:worker]
command=php artisan queue:work redis --tries=1 --sleep=3  ; the program (relative uses PATH, can take args)
process_name=%(program_name)s_%(process_num)02d           ; process_name expr (default %(program_name)s)
numprocs=2                                                ; number of processes copies to start (def 1)
directory=/home/projectname                               ; directory to cwd to before exec (def no cwd)
autostart=true                                            ; start at supervisord start (default: true)
autorestart=true                                          ; retstart at unexpected quit (default: true)
user=root                                                 ; setuid to this UNIX account to run the program
redirect_stderr=true                                      ; redirect proc stderr to stdout (default false)
stdout_logfile=/home/projectname/storage/logs/worker.log  ; stdout log path, NONE for none; default AUTO

設定ファイル編集の Tips

  • [inet_http_server]の欄をアンコメントすれば Supervisor をブラウザで管理できる
  • numprocs=はサーバーのコア数と同じにするのが一般的
  • directory=はフルパスにしないと正常に動作しない

supervisord 再起動

sudo systemctl restart supervisord.service

config の再読み込み

sudo supervisorctl reread
reloadではないので注意

設定の反映

sudo supervisorctl update

サービス(ワーカー)起動

sudo supervisorctl start worker:*

確認

sudo supervisorctl status

worker:worker_00 RUNNING pid 667, uptime 2:40:00

のように表示されればOK

ログの場所

  • ワーカー(上述の設定ファイルの記述先)
    • /home/project/storage/logs
  • Supervisor 自体
    • /var/log/supervisor/supervisord.log

非同期でメール送信する

以前投稿したメール送信処理記事のコードを少しだけ変更して、メール送信時にキューを使用するようにする。

app/Mail/RegisterShipped.php
class RegisterApplied extends Mailable implements ShouldQueue
{

と言ってもShouldQueueインターフェイスを追加するだけ。

正常にキューを使用しているか確認

  1. Supervisor を停止
  2. メール送信をする
  3. メールが届いていないことを確認
  4. Supervisor を起動
  5. メールが届いていることを確認

おまけ

実際には Ansible を使って Redis と Supervisor を導入した。
Supervisor 用のsupervisorctlというモジュールが用意されているが、staterereadを指定できなかったので、commandモジュールを使用した。

main.yml
...
- name: reread supervisor
  command: supervisorctl reread
...