Supervisorを導入した経緯
cronによる実行の場合、最小実行単位は「毎分」になります。
しかし、1分に1回の実行しかできません。
処理時間が10秒程度のプロセスの場合、残り50秒程度が無駄な時間となってしまうことになります。
今回は継続的に処理をする必要があったため、Supervisorを導入しました。
entered fatal state too many start retries too quickly が発生
このエラーを吐いて、新しいプロセスが立ち上がらなくなりました。
その時に実行していた処理を以下に記載します。
サービスの構成
html
├─ .git
| └─...
├─ src
│ └─ app
| └─ Console
| └─ Commands
| └─ Sqs
| └─ SqsEmail.php
└─ etc
└─ docker
└─ worker
└─ supervisord.conf
└─ Dockerfile
└─ docker-entrypoint.sh
FATAL発生時にsupervisorで動作させたプログラム
<?php
namespace App\Console\Commands\Sqs;
use Illuminate\Console\Command;
use packages\UseCase\Queue\PopEmailQueueUseCaseInterface;
class SqsEmail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sqs:email';
/**
* The console command description.
*
* @var string
*/
protected $description = 'EMAILキューを処理する';
/**
* Execute the console command.
*/
public function handle(
PopEmailQueueUseCaseInterface $emailQueueUseCase,
): void {
// 送信処理
$emailQueueUseCase->handle();
}
}
FATAL発生時のsupervisorの設定
※別途dockerは用意
[supervisord]
nodaemon=true
user=root
logfile=/tmp/stdout
pidfile=/xxx/html/tmp/supervisord/supervisord.pid
[program:queue-process-email]
process_name=%(program_name)s_%(process_num)02d
command=php artisan sqs:email --verbose
autostart=true
autorestart=true
user=root
numprocs=2
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
stopwaitsecs=300
発生原因
発生要因は以下の2つでした。
■1つ目
「プロセスが正常に起動したと判定されるまでの秒数(startsecs)」と「プロセスの起動が失敗したときのリトライ回数(startretries)」
を意識せず利用していた。定義もしていなかった。
定義しなかった場合、デフォルトはそれぞれ、
- startsecsが1秒
- startretriesが3回
とのこと。
この場合、「3回連続でプロセスが1秒以内に終了してしまうと上記の、FATALエラーが発生してしまう」ということ。
■2つ目
supervisorで実行している
command=php artisan sqs:email --verbose
のコマンドで実行されるタスクが1秒未満で終わる可能性を考えていなかったこと。
これらが原因でFATALエラーが発生してしまったので、以下のように処理を修正しました。
<?php
namespace App\Console\Commands\Sqs;
use Illuminate\Console\Command;
use packages\UseCase\Queue\PopEmailQueueUseCaseInterface;
class SqsEmail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sqs:email';
/**
* The console command description.
*
* @var string
*/
protected $description = 'EMAILキューを処理する';
/**
* Execute the console command.
*/
public function handle(
PopEmailQueueUseCaseInterface $emailQueueUseCase,
): void {
// 送信処理
$emailQueueUseCase->handle();
// 処理が早く終わりすぎると、supervisorが異常になるので、1-2秒スリープする
sleep(mt_rand(1, 2)); // 追加(重要)
}
}
[supervisord]
nodaemon=true
user=root
logfile=/tmp/stdout
pidfile=/xxx/html/tmp/supervisord/supervisord.pid
[program:queue-process-email]
process_name=%(program_name)s_%(process_num)02d
command=php artisan sqs:email --verbose
autostart=true
autorestart=true
user=root
numprocs=2
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
stopwaitsecs=300
startretries=5 ; 追加(リトライカウントを3から5に上げる)
startsecs=1 ; 追加(明示的に示しておく)
最後に
supervisorは正常に動作しているときは、自動的に新しいプロセスを生み出してくれますが、
FATALが発生した場合は自動的に新しいプロセスを生み出してくれなくなります。
利用する際は気をつけましょう。