Laravel Horizon 拾取作业延迟1-2秒问题排查求助
针对Horizon作业拾取延迟的优化方案
直接调用handle是同步执行,没有队列调度开销,而Horizon的延迟主要来自队列监听策略、进程配置、Redis交互这几个环节。结合你的配置,可从以下方向调整:
1. 调整队列进程的负载均衡策略
你当前用的balance => 'auto'会根据队列长度动态调进程数,但调整有冷却时间,突发小量请求可能来不及扩容。建议:
- 对低延迟需求的
supervisor-fast-jobs,把balance改成'simple',避免动态调整的开销:'supervisor-fast-jobs' => [ // ... 其他配置保留 'balance' => 'simple', // 固定分配进程到队列,无动态调整延迟 ] - 缩短
balanceCooldown时间(生产环境当前是3秒),改成1秒让进程调整更灵敏:'balanceCooldown' => 1,
2. 增加最小进程数,避免冷启动延迟
生产环境下你的minProcesses都是1,无作业时进程会缩到1,新作业进来要等扩容。对supervisor-fast-jobs这类队列,直接设置minProcesses为日常最小需求数:
'supervisor-fast-jobs' => [ 'minProcesses' => 5, // 根据业务量调整,避免进程从零启动 'maxProcesses' => 150, // ... 其他配置 ]
3. 优化Redis队列的阻塞监听配置
Redis队列默认是阻塞式读取,但如果配置了sleep参数,空闲时进程会休眠,新作业进来要等休眠结束。检查config/queue.php的Redis连接:
'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, 'block_for' => null, // 设为null表示无限阻塞,有作业立即响应 ],
4. 确保作业投递到正确的快速队列
控制器触发作业时,明确指定redis-short-running或on-demand-runs-now这类快速队列:
// 控制器代码示例 YourJob::dispatch()->onQueue('redis-short-running');
如果默认投到default队列(属于regular-jobs),进程优先级和数量都不如快速队列,必然有延迟。
5. 降低Horizon监控开销
你的配置开启了metrics,如果不需要实时监控,可减少快照保留数量:
'metrics' => [ 'trim_snapshots' => [ 'job' => 24, // 减少到24小时 'queue' => 24, ], ]
同时确保目标作业在silenced列表里,避免Horizon记录过多日志拖慢速度。
6. 优化Redis连接性能
- 确保Redis和应用在同一服务器,避免跨网络延迟
- 在
config/database.php的Redis配置里开启持久化连接,减少TCP握手开销:'redis' => [ 'default' => [ // ... 其他配置 'persistent' => true, // 开启持久化连接 ], ]
7. 调整进程重启策略
当前maxTime设置(比如fast队列60秒)会让进程每60秒重启一次,频繁重启会导致拾取延迟。可延长maxTime或设置maxJobs:
'supervisor-fast-jobs' => [ 'maxTime' => 300, // 改成5分钟 'maxJobs' => 1000, // 处理1000个作业后重启,平衡内存泄漏和重启频率 ]
8. 开启Fast Termination
当前fast_termination是false,开启后Horizon重启时无需等待所有进程结束,新进程可立即处理作业,减少部署/重启时的延迟:
'fast_termination' => true,
验证步骤
调整配置后重启Horizon:
php artisan horizon:terminate php artisan horizon
再用Carbon记录从dispatch到handle开始的时间差,对比延迟是否减少。
内容的提问来源于stack exchange,提问作者Ryan H

