#はじめに
Laravel Horizonを使う前に、Laravelの基本的なキューサービスに慣れておくことをおすすめします。HorizonはLaravelのキューに追加機能を提供するため、基本的なキュー機能を理解していないと混乱する可能性があります。
Laravel Horizonは、Laravelで動作するRedisキューのための美しいダッシュボードとコードベースの設定を提供します。Horizonを使うと、ジョブのスループット、実行時間、失敗したジョブなど、キューシステムの主要なメトリクスを簡単に監視できます。
Horizonを使うと、キューワーカーの設定を単一のシンプルな設定ファイルにまとめられます。アプリケーションのワーカー設定をバージョン管理されたファイルで定義することで、デプロイ時にキューワーカーのスケールや変更を簡単に行えます。
#インストール
Laravel Horizonはキューの動力源としてRedisを使用するため、アプリケーションのconfig/queue.php設定ファイルでキュー接続がredisに設定されていることを確認してください。
Composerパッケージマネージャーを使ってHorizonをプロジェクトにインストールできます:
composer require laravel/horizon
Horizonをインストールしたら、horizon:install Artisanコマンドでアセットを公開してください:
php artisan horizon:install
#設定
Horizonのアセットを公開すると、主要な設定ファイルはconfig/horizon.phpに配置されます。この設定ファイルでアプリケーションのキューワーカーオプションを設定できます。各設定オプションには目的の説明があるので、よく確認してください。
Horizonは内部でhorizonという名前のRedis接続を使用します。この接続名は予約されているため、database.php設定ファイルやhorizon.phpのuseオプションの値として他のRedis接続に割り当てないでください。
#環境設定
インストール後、まず慣れておくべき主要なHorizon設定はenvironmentsオプションです。このオプションはアプリケーションが動作する環境の配列で、それぞれの環境に対するワーカープロセスの設定を定義します。デフォルトではproductionとlocal環境が含まれていますが、必要に応じて環境を追加できます:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
Horizonを起動すると、アプリケーションが動作している環境のワーカープロセス設定を使用します。通常、環境はAPP_ENVの環境変数の値で決まります。例えば、デフォルトのlocal環境は3つのワーカープロセスを起動し、各キューに割り当てるワーカープロセス数を自動でバランスします。デフォルトのproduction環境は最大10のワーカープロセスを起動し、同様に自動でバランスします。
horizon設定ファイルのenvironments部分には、Horizonを実行する予定のすべての環境のエントリを含めるようにしてください。
#スーパーバイザー
Horizonのデフォルト設定ファイルを見ると、各環境は1つ以上の「スーパーバイザー」を含められます。デフォルトではsupervisor-1と名付けられていますが、自由に名前を変更できます。スーパーバイザーはワーカープロセスのグループを監督し、キュー間でワーカープロセスをバランスする役割を持ちます。
特定の環境に新しいワーカープロセスグループを定義したい場合は、追加のスーパーバイザーを設定できます。これは、アプリケーションで使う特定のキューに対して異なるバランス戦略やワーカープロセス数を設定したい場合に便利です。
#メンテナンスモード
アプリケーションがメンテナンスモードの場合、Horizonはキューのジョブを処理しません。ただし、スーパーバイザーのforceオプションがHorizon設定ファイルでtrueに設定されている場合は例外です:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'force' => true,
],
],
],
#デフォルト値
Horizonのデフォルト設定ファイルにはdefaultsオプションがあります。これはアプリケーションのスーパーバイザーのデフォルト値を指定します。スーパーバイザーの設定は各環境の設定にマージされるため、設定の重複を避けられます。
#バランス戦略
Laravelのデフォルトキューシステムとは異なり、Horizonは3つのワーカーバランス戦略から選べます:simple、auto、false。simple戦略はジョブをワーカープロセス間で均等に分配します:
'balance' => 'simple',
auto戦略は設定ファイルのデフォルトで、キューの現在の負荷に応じてワーカープロセス数を調整します。例えば、notificationsキューに1,000件の保留ジョブがあり、renderキューが空の場合、Horizonはnotificationsキューにより多くのワーカーを割り当て、キューが空になるまで続けます。
auto戦略を使う場合、minProcessesとmaxProcessesオプションでHorizonがスケールアップ・ダウンするワーカープロセスの最小・最大数を制御できます:
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'tries' => 3,
],
],
],
autoScalingStrategy設定は、Horizonがキューをクリアするのにかかる合計時間(time戦略)か、キュー内のジョブ数(size戦略)に基づいてワーカープロセスを割り当てるかを決めます。
balanceMaxShiftとbalanceCooldownは、Horizonがワーカー需要に応じてどれくらい速くスケールするかを決めます。上記の例では、最大で3秒ごとに1つのプロセスが作成または削除されます。アプリケーションのニーズに合わせて調整可能です。
balanceオプションがfalseの場合、Laravelのデフォルト動作が使われ、キューは設定ファイルに記載された順に処理されます。
#ダッシュボードの認可
Horizonダッシュボードは/horizonルートからアクセスできます。デフォルトではlocal環境でのみアクセス可能です。ただし、app/Providers/HorizonServiceProvider.phpファイル内に認可ゲートが定義されており、非ローカル環境でのアクセス制御を行います。このゲートは必要に応じて変更して、Horizonへのアクセスを制限できます:
/**
* Horizonのゲートを登録します。
*
* このゲートは非ローカル環境でHorizonにアクセスできるユーザーを決定します。
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
#代替認証戦略
Laravelは認証済みユーザーを自動的にゲートクロージャに注入します。もしIP制限など別の方法でHorizonのセキュリティを提供している場合、ユーザーは「ログイン」不要かもしれません。その場合は、上記のfunction (User $user)のクロージャのシグネチャをfunction (User $user = null)に変更し、Laravelに認証を不要にさせる必要があります。
#サイレントジョブ
アプリケーションやサードパーティパッケージがディスパッチする特定のジョブを表示したくない場合があります。これらのジョブが「完了したジョブ」リストを占有しないように、サイレント化できます。始めるには、ジョブのクラス名をアプリケーションのhorizon設定ファイルのsilencedオプションに追加してください:
'silenced' => [
App\Jobs\ProcessPodcast::class,
],
または、サイレント化したいジョブがLaravel\Horizon\Contracts\Silencedインターフェイスを実装していれば、silenced設定配列に含まれていなくても自動的にサイレント化されます:
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// ...
}
#Horizonのアップグレード
Horizonの新しいメジャーバージョンにアップグレードする際は、アップグレードガイドをよく確認してください。また、どのバージョンにアップグレードする場合でも、Horizonのアセットを再公開する必要があります:
php artisan horizon:publish
アセットを最新に保ち、将来のアップデートで問題を避けるために、composer.jsonのpost-update-cmdスクリプトにvendor:publish --tag=laravel-assetsコマンドを追加できます:
{
"scripts": {
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
]
}
}
#Horizonの実行
アプリケーションのconfig/horizon.php設定ファイルでスーパーバイザーとワーカーを設定したら、horizon ArtisanコマンドでHorizonを起動できます。このコマンドは現在の環境のすべての設定済みワーカープロセスを起動します:
php artisan horizon
horizon:pauseとhorizon:continue ArtisanコマンドでHorizonプロセスを一時停止・再開できます:
php artisan horizon:pause
php artisan horizon:continue
horizon:pause-supervisorとhorizon:continue-supervisor Artisanコマンドで特定のHorizonスーパーバイザーを一時停止・再開できます:
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue-supervisor supervisor-1
horizon:status ArtisanコマンドでHorizonプロセスの現在の状態を確認できます:
php artisan horizon:status
horizon:terminate ArtisanコマンドでHorizonプロセスを優雅に終了できます。現在処理中のジョブは完了し、その後Horizonは停止します:
php artisan horizon:terminate
#Horizonのデプロイ
Horizonを実際のサーバーにデプロイする準備ができたら、php artisan horizonコマンドを監視し、予期せず終了した場合に再起動するプロセスモニターを設定してください。以下でプロセスモニターのインストール方法を説明します。
デプロイ時には、Horizonプロセスを終了させてプロセスモニターに再起動させ、コード変更を反映させるよう指示してください:
php artisan horizon:terminate
#Supervisorのインストール
Supervisor は Linux オペレーティングシステム用のプロセス監視ツールで、horizon プロセスが停止した場合に自動的に再起動します。Ubuntu に Supervisor をインストールするには、以下のコマンドを使用できます。Ubuntu 以外の環境の場合は、お使いのOSのパッケージマネージャーで Supervisor をインストールできる可能性があります。
sudo apt-get install supervisor
Supervisor の設定が難しい場合は、Laravel Forge の利用を検討してください。Laravel プロジェクト向けに Supervisor を自動でインストール・設定してくれます。
#Supervisor の設定
Supervisor の設定ファイルは通常、サーバーの /etc/supervisor/conf.d ディレクトリに保存されます。このディレクトリ内に、プロセスの監視方法を指示する設定ファイルを複数作成できます。例えば、horizon プロセスを起動・監視する horizon.conf ファイルを作成してみましょう。
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600
Supervisor の設定を定義する際は、stopwaitsecs の値が最長実行ジョブの処理時間より長くなるようにしてください。そうしないと、ジョブが完了する前に Supervisor が強制終了してしまう可能性があります。
上記の例は Ubuntu ベースのサーバー向けですが、Supervisor の設定ファイルの場所や拡張子は他のサーバーOSによって異なる場合があります。詳細はサーバーのドキュメントを参照してください。
#Supervisor の起動
設定ファイルを作成したら、以下のコマンドで Supervisor の設定を更新し、監視対象のプロセスを起動できます。
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
Supervisor の実行に関する詳細は、Supervisor のドキュメントを参照してください。
#タグ
Horizon では、ジョブ(メール送信、ブロードキャストイベント、通知、キューイングされたイベントリスナーなど)に「タグ」を割り当てられます。実際、Horizon はジョブに関連付けられた Eloquent モデルに基づいて、多くのジョブに自動的かつ賢くタグを付けます。例えば、以下のジョブを見てみましょう。
<?php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class RenderVideo implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* 新しいジョブインスタンスを作成します。
*/
public function __construct(
public Video $video,
) {}
/**
* ジョブを実行します。
*/
public function handle(): void
{
// ...
}
}
このジョブが id 属性が 1 の App\Models\Video インスタンスと共にキューに入れられた場合、自動的に App\Models\Video:1 というタグが付与されます。これは Horizon がジョブのプロパティから Eloquent モデルを探し、見つかった場合はモデルのクラス名と主キーを使って賢くタグ付けするためです。
use App\Jobs\RenderVideo;
use App\Models\Video;
$video = Video::find(1);
RenderVideo::dispatch($video);
#ジョブの手動タグ付け
キューイング可能なオブジェクトに対してタグを手動で定義したい場合は、クラスに tags メソッドを定義できます。
class RenderVideo implements ShouldQueue
{
/**
* ジョブに割り当てるタグを取得します。
*
* @return array<int, string>
*/
public function tags(): array
{
return ['render', 'video:'.$this->video->id];
}
}
#イベントリスナーの手動タグ付け
キューイングされたイベントリスナーのタグを取得する際、Horizon は自動的にイベントインスタンスを tags メソッドに渡すため、イベントデータをタグに追加できます。
class SendRenderNotifications implements ShouldQueue
{
/**
* リスナーに割り当てるタグを取得します。
*
* @return array<int, string>
*/
public function tags(VideoRendered $event): array
{
return ['video:'.$event->video->id];
}
}
#通知
Horizon で Slack や SMS 通知を設定する場合は、該当通知チャネルの前提条件を必ず確認してください。
キューの待機時間が長い場合に通知を受け取りたい場合は、Horizon::routeMailNotificationsTo、Horizon::routeSlackNotificationsTo、Horizon::routeSmsNotificationsTo メソッドを使えます。これらはアプリケーションの App\Providers\HorizonServiceProvider の boot メソッド内で呼び出せます。
/**
* アプリケーションサービスをブートストラップします。
*/
public function boot(): void
{
parent::boot();
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('[email protected]');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
#通知の待機時間閾値の設定
アプリケーションの config/horizon.php 設定ファイルで「長い待機時間」とみなす秒数を設定できます。このファイルの waits オプションで、接続ごと・キューごとの長い待機時間の閾値を制御可能です。未定義の接続・キューの組み合わせはデフォルトで60秒になります。
'waits' => [
'redis:critical' => 30,
'redis:default' => 60,
'redis:batch' => 120,
],
#メトリクス
Horizon にはジョブやキューの待機時間、スループットに関する情報を提供するメトリクスダッシュボードがあります。このダッシュボードを更新するには、アプリケーションの スケジューラーを使って、5分ごとに Horizon の snapshot Artisan コマンドを実行するよう設定してください。
/**
* アプリケーションのコマンドスケジュールを定義します。
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('horizon:snapshot')->everyFiveMinutes();
}
#失敗したジョブの削除
失敗したジョブを削除したい場合は、horizon:forget コマンドを使えます。horizon:forget は失敗ジョブの ID または UUID を唯一の引数として受け取ります。
php artisan horizon:forget 5
#キューからのジョブ削除
アプリケーションのデフォルトキューからすべてのジョブを削除したい場合は、horizon:clear Artisan コマンドを使えます。
php artisan horizon:clear
特定のキューからジョブを削除したい場合は、queue オプションを指定できます。
php artisan horizon:clear --queue=emails