Laravel Horizon 监控指标解读

解读

在国内一线/二线互联网公司的 PHP 面试中,Horizon 已不再是“加分项”,而是“必问项”。面试官抛出该问题,通常想验证三件事:

  1. 你是否真的在生产环境用过 Horizon,而不是只跑过 demo;
  2. 你能否把 Redis 队列的抽象指标翻译成业务语言,帮助产品、运维快速定位问题;
  3. 你能否基于指标做容量预估与故障自愈,而不是等报警了才“拍脑袋”扩容。
    因此,回答时要“说人话”:先给指标定义,再给业务阈值,最后给落地案例。切忌背文档,否则会被追问“你们当时把 balanceauto 改成 simple 后,QPS 掉了 12%,为什么?”

知识点

  1. Horizon 架构:Lua 脚本 + Redis 原语(zsetlisthash)实现的轻量级队列仪表盘,零额外存储依赖。
  2. 指标分层:
    全局层——Jobs Per Minute、Throughput、Wait Time、Peak Load;
    队列层——吞吐量、失败率、延迟分布、内存占用、阻塞时长;
    工作进程层——Busy / Idle 比例、重启次数、CPU 抢占、内存泄漏;
    任务实例层——Runtime、Tries、Timeout、Exceptions、Retry Until。
  3. 国内常用阈值(电商大促场景):
    Wait Time > 500 ms 即触发扩容;
    失败率 > 1% 且持续 2 周期即停流量入口;
    单 Worker 内存 > 256 MB 且连续 3 周期即强制重启。
  4. 关键配置:
    balance = auto 依据等待时间动态 fork;
    maxProcesses 受限于 Kubernetes 的 cgroup 最大进程数;
    triesretry_after 必须大于任务最大执行时间,防止 Redis 出现“幽灵任务”。
  5. 监控闭环:Horizon 指标 → Prometheus Exporter(horizon-exporter 包)→ Grafana 看板 → 阿里云 SLS 日志 → 钉钉/飞书机器人 → 自动调整 HPA 的 replicas

答案

Horizon 提供的监控指标可拆成“快慢、多少、错重”三大维度,每个维度我都曾在生产环境落地,具体如下:

  1. 快慢——Wait 与 Runtime
    Wait 时间反映队列拥堵程度,我们双 11 把“订单关闭”队列的 Wait 阈值设在 300 ms,超过即通过阿里云 ESS 横向扩容 30%。Runtime 看任务自身性能,我曾发现某退款任务 Runtime 从 1.2 s 暴涨到 8 s,最终定位是 SQL 没走到联合索引,加索引后降回 1 s。
  2. 多少——Throughput、Peak Load、Memory
    Throughput 分钟级曲线可与网关 QPS 做对比,验证削峰效果。Peak Load 告诉我们“最长队列”在哪,曾发现“秒杀结果”队列峰值 20 k,远超其他队列,于是单独拆到一个 8 G 内存的 Redis 分片。Memory 指标直接决定 Kubernetes 的 resources.limits.memory,我们默认 128 MB,超过即触发 OOMKill 并自动重启。
  3. 错重——Failed、Retries、Exceptions
    失败率 > 1% 立即熔断流量入口,这是公司 SRE 红线。Horizon 的 recent_failures 面板能快速定位异常方法,我们曾靠它 3 分钟锁定拼多多回调验签失败,原因是 openssl 版本升级后证书链顺序变更。Retries 次数与 retry_after 必须大于任务最大执行时间,否则会出现任务重复执行,我们规定 retry_after = max_runtime + 30 s

借助以上指标,我们把 2023 年 618 大促的订单峰值从 8 k/s 扛到 22 k/s,队列平均等待时间仍保持在 180 ms 以内,P99 延迟 < 2 s,全年零重大故障。

拓展思考

  1. 如果 Redis 是 Cluster 模式,Horizon 的 Lua 脚本会出现 CROSSSLOT 错误,如何在不改 Horizon 源码的前提下解决?
    答:给队列 key 加 {hash_tag} 强制同一 slot,例如 queues:{order}:default,同时把 horizon:metrics 也加上相同 tag,保证所有操作落在同一节点。
  2. 当业务需要“优先级队列”时,Horizon 原生不支持,你会如何扩展?
    答:在 Job 类里实现 horizonTag() 方法返回 priority:high,再写自定义的 QueueManager,把 high 标签任务插到队列头部;同时给 Horizon 增加 priority 维度指标,用 Prometheus 记录 high/normal 的 Wait 时间差异。
  3. 国内监管要求“日志留痕 6 个月”,Horizon 的指标只存 7 天,如何低成本合规?
    答:写一条每分钟跑的 Artisan Command,把 horizon:snapshot 落盘到阿里云 OSS(对象存储),按天分区,压缩后单文件 < 50 MB,6 个月总成本 < 30 元,查询时用 Athena 直接跑 SQL,即可在 10 s 内返回任意历史指标。