首页
/ Coolify 队列监控实践:Laravel Horizon 指标快照(horizon:snapshot)与快照保留期配置

Coolify 队列监控实践:Laravel Horizon 指标快照(horizon:snapshot)与快照保留期配置

2026-09-06 16:19:52作者:牧宁李

Coolify 基于 Laravel Horizon 管理其全部后台任务(部署、备份、通知等)的队列工作进程。然而,即使 php artisan horizon 正常启动,Horizon 控制台的 Metrics 面板也可能长时间一片空白——原因是指标曲线完全由 horizon:snapshot 生成的快照数据驱动,而快照本身必须显式注册到 Laravel 任务调度器中才会周期性产生。本文以 Coolify 仓库中 .claude/skills/configuring-horizon/references/metrics.md 参考文档为主线,结合 config/horizon.phpapp/Console/Kernel.php 的真实实现,讲清 Horizon 指标"为什么是空的"、"快照该如何注册"以及"快照保留期参数到底代表什么"这三个核心问题。读完本文,你可以直接定位并修复 Horizon 指标面板空白、快照调度缺失、保留期配置误解这三类典型问题。

指标面板为空的根因:horizon 命令不产生快照数据

参考文档给出的第一个关键结论是:运行 horizon artisan 命令并不会自动填充指标数据

Horizon 控制台的指标图(Jobs/Queues 处理速率、失败率等曲线)并不是实时从队列读取的,而是从"快照(snapshot)"数据构建出来的。快照由独立的 horizon:snapshot 命令生成,且必须通过 Laravel 调度器每 5 分钟执行一次,指标曲线才会有持续的数据点:

// 快照必须注册到调度器,而不是手动执行
$schedule->command('horizon:snapshot')->everyFiveMinutes();

也就是说,php artisan horizon 只负责启动 supervisor 进程消费队列,它不触碰快照存储。如果你只在启动 Horizon 后打开 /horizon 页面,Metrics 图表自然没有任何数据点。这也是参考文档中 "Metrics dashboard stays blank until horizon:snapshot is scheduled" 这一条目的完整含义:快照调度是指标可视化的前置依赖,而非可选增强。

Coolify 中的真实调度注册

在 Coolify 仓库中,快照的调度注册位于 app/Console/Kernel.php,且按开发/生产环境采用了不同频率:

// app/Console/Kernel.php
if (isDev()) {
    // Instance Jobs
    $this->scheduleInstance->command('horizon:snapshot')->everyMinute();
    ...
} else {
    // Instance Jobs
    $this->scheduleInstance->command('horizon:snapshot')->everyFiveMinutes();
    ...
}

可以看到:

  • 开发环境(isDev()horizon:snapshot 每分钟执行一次,便于本地快速看到指标曲线变化;
  • 生产环境:每 5 分钟执行一次,这是参考文档中推荐的标准节奏("must be scheduled to run every 5 minutes via Laravel's scheduler")。

而调度器本身的运行由容器内的 s6-overlay 服务承载:docker/production/etc/s6-overlay/s6-rc.d/scheduler-worker/rundocker/development/etc/s6-overlay/s6-rc.d/scheduler-worker/run 均执行 php artisan schedule:work。从源码结构看,这意味着 horizon:snapshot 并非独立 cron,而是随 schedule:work 常驻进程按上表频率触发——排查"生产环境快照缺失"时,应当先确认 scheduler-worker 服务是否存活,而不是只盯着 horizon 进程。

用调度器注册快照,而不是手动执行

参考文档的第二条告诫是:手动执行一次快照只能让面板短暂有数据,无法维持更新

  • 手动 php artisan horizon:snapshot 一次 → 只写入一个时间点的快照,面板短暂出现一个数据点;
  • 调度器周期性执行 → 指标曲线随时间连续生长,且与保留策略(见下一节)配合形成滚动窗口。

参考文档同时提醒:调度器注册语法在 Laravel 10 与 Laravel 11+ 之间存在差异。这一点在 Coolify 中可以直接确认:composer.json 中声明了 "laravel/framework": "^12.65.0""laravel/horizon": "^5.48.2",即项目运行于 Laravel 12 之上,因此 app/Console/Kernel.php 使用的是 Laravel 11+ 风格:

// Laravel 11+(Coolify 当前实际写法)
$this->scheduleInstance->command('horizon:snapshot')->everyFiveMinutes();

而 Laravel 10 及以下项目的等价写法是传统的调度器闭包:

// Laravel 10 及以下
protected function schedule(Schedule $schedule): void
{
    $schedule->command('horizon:snapshot')->everyFiveMinutes();
}

两者的语义一致(每 5 分钟触发一次 horizon:snapshot),差异在于调度器的注册入口与文件组织方式。如果你的代码库从旧版 Laravel 升级而来,应检查 schedule 定义位置是否已迁移,这正是参考文档要求先搜索 "horizon metrics snapshot" 确认语法的原因——用错版本语法会导致调度静默失效,指标面板依旧空白。

metrics.trim_snapshots 是快照条数,不是时间单位

参考文档指出的第三个易错点极具误导性风险:config/horizon.phpmetrics.trim_snapshotsjobqueue 值表示"保留多少条快照",而不是分钟或小时数

Coolify 仓库中的实际配置(config/horizon.php)如下:

// config/horizon.php
'metrics' => [
    'trim_snapshots' => [
        'job' => 24,
        'queue' => 24,
    ],
],

其上方注释也明确写道:该值与 horizon:snapshot 的调度周期结合使用,共同决定指标保留时长("This will get used in combination with Horizon's horizon:snapshot schedule to define how long to retain metrics")。

按参考文档的算法:

参数 快照频率 等效保留时长
trim_snapshots.job 24 每 5 分钟 1 次 24 × 5 min = 2 小时
trim_snapshots.queue 24 每 5 分钟 1 次 24 × 5 min = 2 小时

即默认配置下,Job 与 Queue 的指标曲线只展示最近 2 小时的历史。需要更长历史时,应增大这两个数值,代价是 Redis 内存占用上升——每个快照都会以字符串形式写入 Horizon 前缀下的 Redis 键空间。

这里有一个必须区分的易混淆配置:同一文件中的 trim 段(config/horizon.php)里的 recentpendingcompletedrecent_failedfailedmonitored 才是分钟数

// config/horizon.php —— 注意:这里的值单位是分钟
'trim' => [
    'recent' => 60,        // 分钟
    'pending' => 60,       // 分钟
    'completed' => 60,     // 分钟
    'recent_failed' => 10080, // 1 周(分钟)
    'failed' => 10080,
    'monitored' => 10080,
],

trim 控制的是任务条目(recent/pending/completed/failed 列表)的持久化时长,metrics.trim_snapshots 控制的是指标快照条数——两者单位不同、用途不同,混用会导致对"指标为什么只显示 2 小时"产生错误判断。

前提条件与排查清单

结合参考文档与仓库实现,Horizon 指标要正常工作需满足以下前提(均可在仓库中验证):

  1. 队列驱动必须是 Redisconfig/queue.php'default' => env('QUEUE_CONNECTION', 'redis'),默认即 Redis 连接;config/horizon.php 中 Horizon 自身元数据存储也使用 default Redis 连接。参考文档所属的技能指南(SKILL.md)亦明确:Horizon 仅支持 Redis 队列驱动,database、SQS 均不受支持。
  2. horizon:snapshot 必须存在于调度器中,且调度器进程(schedule:work)在运行。Coolify 中对应 app/Console/Kernel.php(开发,每分钟)与 app/Console/Kernel.php(生产,每 5 分钟)。
  3. 保留窗口 = 快照条数 × 快照间隔。默认 24 条 × 5 分钟 = 2 小时;调大 metrics.trim_snapshots.job/queue 可延长曲线历史,需权衡 Redis 内存。
  4. 快照写入是滚动覆盖的,旧快照超出保留条数即被修剪,因此"面板空白"与"曲线只有近期数据"是两种不同现象:前者通常缺调度,后者是保留策略生效。

小结

围绕 metrics.md 参考文档的三个要点,可以在 Coolify 仓库中找到完整落点:指标面板空白源于 horizon:snapshot 未入调度器(对照 app/Console/Kernel.php 的真实注册方式);快照必须靠周期性调度而非手动单次执行,且注册语法需匹配 Laravel 版本(Coolify 为 Laravel 12,采用 11+ 写法);metrics.trim_snapshotsjob/queue(默认各 24 条)是快照条数,与 5 分钟快照间隔相乘得到约 2 小时的曲线保留窗口,与以分钟为单位的 trim 段不可混淆。掌握这三点后,任何 Horizon 指标相关的"空白面板、数据断档、窗口偏短"问题都可以按"调度是否存在 → 调度器是否存活 → 保留条数是否合理"的顺序快速定位。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388