蟻地獄

Twitterに書ききれない長めの文とか書くよ

LaravelのキューワーカーをCloudWatchで監視する

この記事は VALU Advent Calendar 2019 5日目のエントリです。

株式会社VALUのバックエンドエンジニアのMito Memelです。FGOの星4配布はぐっちゃん先輩にしました。

皆さん、Laravelのキューワーカーは監視していますか?公式ドキュメントに書いてあるようにSupervisorに登録しただけで監視した気にはなっていないですよね?

Supervisorはプロセスの死活を見ているだけで、実際にキューが消化されているかは見てくれません。キューが詰まってしまってもなかなか気づきにくいのがキューワーカーの怖いところですよね。タイムアウトを設定していていも、そもそもワーカー本体が何らかの原因で固まってしまうこともあり得ます(というか最近本番環境で発生しました)。そこで、実際にキューが消化されているかをCloudWatchで監視してみます。

キューが消化されているかを確認するには、実際にジョブを投げて処理されるかを見るのが確実です。つまり外形監視ですね。というわけで、まずはCloudWatchに値を投げつけるだけのジョブを作成します。

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

    private $created_at;

    public function __construct()
    {
        $this->created_at = now();
    }

    public function handle()
    {
        $client = App::make('aws')->createClient('cloudwatch');
        $client->putMetricData([
            'Namespace' => config('app.name') . '/' . config('app.env'),
            'MetricData' => [
                [
                    'MetricName' => 'QueueWorkerLatency',
                    'Value' => now()->diffInSeconds($this->created_at),
                    'Unit' => 'Seconds',
                ]
            ],
        ]);
    }
}

__construct()handle()の時間差を測ることで、「ジョブをdispatch()してから実際に処理されるまでの時間」が測れるわけですね。

そしてこのジョブをdispatch()するだけのコマンドを作成してタスクスケジュールに登録します。

class DispatchHeartbeat extends Command
{
    protected $signature = 'heartbeat:dispatch';

    protected $description = 'Heartbeatジョブをキューに積みます';

    public function handle()
    {
        Heartbeat::dispatch();
    }
}

これでCloudWatchでジョブの処理遅延時間をメトリクスとして取得することができるようになりました! f:id:mitomemel:20191204145642p:plain

あとはCloudWatchのアラーム設定でデータの欠落or処理遅延時間の増加を検知してアラートメールを送るなり煮るなり焼くなりしましょう。 これでキューが急に詰まっても安心ですね!