什么是 Horizon
Laravel Horizon 是 Laravel 官方提供的专用于 Redis 队列的监控仪表盘。可以实时查看任务吞吐量、执行时间和失败情况,并通过代码管理 worker 配置。
Horizon 是对队列基础功能的扩展。请先了解队列与任务的基础,再阅读本文。此外,其后端必须是 Redis。
Horizon 使用 Redis 作为队列后端。请确认 config/queue.php 中 QUEUE_CONNECTION 已设置为 redis。目前尚不支持 Redis Cluster。
通过 Composer 安装。
composer require laravel/horizon
安装后发布 Horizon 的资源与配置文件。
php artisan horizon:install
该命令会生成 config/horizon.php 与 app/Providers/HorizonServiceProvider.php。
config/horizon.php 结构
config/horizon.php 集中管理 worker 的所有配置。核心选项是 environments。
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default', 'notifications'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'tries' => 3,
'timeout' => 60,
],
],
'local' => [
'supervisor-1' => [
// 其他项会从 defaults 部分继承
'maxProcesses' => 3,
],
],
],
Horizon 内部会使用一个名为 horizon 的 Redis 连接。请不要在 config/database.php 中把这个名字用于其他连接。
Supervisor(监督者)
每个环境可以有一个或多个“Supervisor”。它们是 worker 组的管理单元,可以在同一个环境下运行多个具有不同队列、平衡策略和进程数的 supervisor。
默认值
defaults 选项可以设置应用于所有 supervisor 的默认值。
'defaults' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'tries' => 1,
'timeout' => 60,
'maxProcesses' => 1,
],
],
维护模式
当应用处于维护模式时,Horizon 默认不会处理任务。如果要强制处理,可以使用 force 选项。
'environments' => [
'production' => [
'supervisor-1' => [
'force' => true,
],
],
],
任务最大重试次数
'environments' => [
'production' => [
'supervisor-1' => [
'tries' => 10,
],
],
],
tries 设为 0 时表示允许无限次重试。
任务超时
'environments' => [
'production' => [
'supervisor-1' => [
'timeout' => 60,
],
],
],
timeout 应比 config/queue.php 中的 retry_after 短几秒。此外,auto 平衡策略下可能会强制中止超过该时间的任务。
退避(重试等待时间)
指定发生异常后到下一次重试的等待秒数。
// 固定值
'backoff' => 10,
// 阶梯式(指数退避)
'backoff' => [1, 5, 10],
平衡策略
Horizon 提供三种 worker 平衡策略。
根据队列负载自动调整 worker 数量。通过 minProcesses 和 maxProcesses 指定范围。'supervisor-1' => [
'balance' => 'auto',
'autoScalingStrategy' => 'time', // 或 'size'
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
time — 根据清空队列的预计时间进行伸缩
size — 根据队列内的任务数进行伸缩
在 auto 策略下,队列的顺序并不代表优先级。如果需要强制优先级,请使用多个 supervisor。
固定 worker 数量,均匀分配给指定的队列。'supervisor-1' => [
'balance' => 'simple',
'processes' => 10,
'queue' => ['default', 'notifications'],
],
上例会为 default 和 notifications 各分配 5 个进程。
严格按照列举的顺序作为优先级。行为与 Laravel 默认的队列系统类似,但会根据积压情况伸缩 worker 数量。'supervisor-1' => [
'balance' => false,
'queue' => ['default', 'notifications'],
'minProcesses' => 1,
'maxProcesses' => 10,
],
default 队列的任务始终优先于 notifications 队列被处理。
仪表盘的授权
Horizon 仪表盘可通过 /horizon 路由访问。本地环境默认对所有人开放,但生产环境需要通过 Gate 定义来限制访问。
编辑 app/Providers/HorizonServiceProvider.php 中的 gate() 方法。
use App\Models\User;
use Illuminate\Support\Facades\Gate;
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
如果无需登录(例如已通过 IP 限制保护),可以把参数改为可选。
Gate::define('viewHorizon', function (User $user = null) {
// 通过 IP 等方式限制的场景
return true;
});
启动 Horizon
基本命令
# 启动
php artisan horizon
# 暂停 / 恢复
php artisan horizon:pause
php artisan horizon:continue
# 暂停 / 恢复指定的 supervisor
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue-supervisor supervisor-1
# 查看状态
php artisan horizon:status
php artisan horizon:supervisor-status supervisor-1
# 优雅关闭
php artisan horizon:terminate
本地开发:自动重启
要在检测到文件变更时自动重启 Horizon,可以使用 horizon:listen 命令。
npm install --save-dev chokidar
php artisan horizon:listen
# Docker / Vagrant 环境下
php artisan horizon:listen --poll
通过 Supervisor 常驻运行
生产环境中,通过 Supervisor 让 Horizon 常驻运行。
安装 Supervisor
sudo apt-get install supervisor
创建配置文件
创建 /etc/supervisor/conf.d/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
stopwaitsecs 需设置为大于最长任务执行时间的值。设置得过小时,Supervisor 会中途强制杀掉任务。
启动 Supervisor
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
部署时
每次部署代码后重启 Horizon 以生效。
php artisan horizon:terminate
只要 Supervisor 中设置了 autostart=true / autorestart=true,退出后会自动重启。
任务管理
Horizon 会自动检测任务关联的 Eloquent 模型,并加上标签。
// 接收 Video 模型(id=1) 的任务 → 自动打上 "App\Models\Video:1" 标签
RenderVideo::dispatch(Video::find(1));
若要手动定义标签,实现 tags() 方法即可。
class RenderVideo implements ShouldQueue
{
/**
* @return array<int, string>
*/
public function tags(): array
{
return ['render', 'video:'.$this->video->id];
}
}
在事件监听器中,tags() 方法会收到事件实例。
class SendRenderNotifications implements ShouldQueue
{
public function tags(VideoRendered $event): array
{
return ['video:'.$event->video->id];
}
}
静默任务
若不希望某些任务在仪表盘的“已完成任务”列表中显示,可以在 config/horizon.php 里将其静默化。
'silenced' => [
App\Jobs\ProcessPodcast::class,
],
// 按标签静默
'silenced_tags' => [
'notifications',
],
也可以实现 Silenced 接口。
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
use Queueable;
// ...
}
指标与监控
Horizon 的指标仪表盘展示任务与队列的吞吐量和执行时间。需要定时执行快照命令来采集数据。
// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('horizon:snapshot')->everyFiveMinutes();
若要清除全部指标数据,执行以下命令。
php artisan horizon:clear-metrics
任务失败通知
当队列的等待时间过长时可以接收到通知。可以在 app/Providers/HorizonServiceProvider.php 的 boot() 方法中配置。
use Laravel\Horizon\Horizon;
public function boot(): void
{
parent::boot();
Horizon::routeMailNotificationsTo('[email protected]');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#ops');
Horizon::routeSmsNotificationsTo('15556667777');
}
等待时间阈值
在 config/horizon.php 的 waits 选项中配置触发通知的等待秒数。
'waits' => [
'redis:critical' => 30, // 等待超过 30 秒即通知
'redis:default' => 60,
'redis:batch' => 120,
],
设为 0 表示禁用该队列的通知。
失败任务的管理
失败任务可以通过 ID 或 UUID 删除。
# 删除指定的失败任务
php artisan horizon:forget 5
# 删除全部失败任务
php artisan horizon:forget --all
要清空队列中的任务,可以使用以下命令。
# 清空默认队列
php artisan horizon:clear
# 清空指定队列
php artisan horizon:clear --queue=emails
Horizon 主版本升级前,请务必参考升级指南。
相关页面
队列与任务
Laravel 队列的基础。任务的创建、派发、批处理与失败处理。
Redis
Horizon 后端所需的 Redis 配置与用法。