Laravel队列不处理新任务的主因是queue:work进程未重启以加载新代码;需通过supervisorctl restart或设置足够stopwaitsecs+killasgroup确保旧进程退出并拉起新实例,同时验证Redis信号广播与队列参数对齐。

Supervisor 不是“配完就能跑”,它只负责拉起和保活进程,不感知代码更新。旧的 queue:work 进程会一直拿着旧的类加载器、OPcache 和配置,除非它自己退出并被 Supervisor 重新拉起——这点最容易被忽略。
为什么 supervisorctl status 显示 RUNNING 却不处理新任务
常见错误现象:部署新代码后,supervisorctl status 看起来一切正常,但刚 dispatch 的任务没进日志、也没进 failed_jobs 表,像被吞了一样。
- 根本原因不是 Supervisor 没生效,而是
queue:work进程仍在运行旧实例,未加载新代码 - 检查是否用了
--daemon模式(Laravel 8+ 默认启用),否则queue:restart信号无效 - 确认缓存驱动不是
array:用Cache::get('laravel_queue_restart_signal')在tinker中验证信号是否写入成功;若返回 null,说明信号压根没广播出去 - Redis 驱动下,确保
config/cache.php中default指向的是redis或file,而非array
supervisor 配置里 numprocs 和 --queue 参数必须对齐
多个 worker 进程如果都监听同一个队列,没问题;但如果按业务拆了队列(比如 emails、reports、moderation),每个队列就得有独立的 [program] 块,且 command 中的 --queue=xxx 必须显式指定。
-
numprocs=4+--queue=default→ 4 个进程争抢default队列,适合通用任务 - 要监听多个队列,写成
--queue=emails,reports,注意中间无空格 - 若为事件监听器单独启进程(如
do_test_event),必须在command中写死--queue=do_test_event,不能依赖默认值 - Redis 最大连接数要留余量:假设
redis.conf中maxclients=200,那所有numprocs总和别超 15~20,否则出现Connection refused或超时
stopwaitsecs 和 killasgroup 是优雅退出的关键
Supervisor 默认用 SIGTERM 终止进程,但 Laravel 的 queue:work 只有在完成当前任务后才响应 queue:restart 信号。如果 stopwaitsecs 太小,Supervisor 会强行发 SIGKILL 杀掉正在跑的任务,导致数据不一致或任务丢失。
- 务必设
stopwaitsecs=3600(或至少 600),给长任务留足收尾时间 - 必须加
killasgroup=true和stopasgroup=true,否则子进程(如 Guzzle HTTP 请求、FFmpeg 调用)可能残留成孤儿进程 - 避免使用
autorestart=unexpected:它只在非 0 退出码时重启,而正常退出(如收到 restart 信号)是 0 码,会导致 Supervisor 不拉新进程 - 日志路径要可写:
stdout_logfile=/var/log/laravel-worker.log对应目录需chown www-data:www-data并chmod 755
没有 Supervisor 时怎么让 queue:work 不挂
共享主机、轻量 VPS 或测试环境常无法装 Supervisor,此时只能靠 cron + 脚本轮询兜底。
- 每分钟执行一次检查脚本:
ps aux | grep 'queue:work' | grep -v grep || php /var/www/artisan queue:work --once -
--once模式虽不优雅,但能避免长期驻留导致的内存泄漏或配置陈旧问题 - 搭配
--max-jobs=100和--max-time=300防止单次执行过久,例如:php artisan queue:work --once --max-jobs=100 --max-time=300 - 注意:这种模式无法响应
queue:restart,部署后需手动 kill 旧进程或等它自然结束
真正难的不是写对配置,而是理解 Supervisor 和 Laravel 队列之间没有“热更新”协议——它只管生不管养,代码更新、配置变更、甚至 Redis 连接参数调整,都得靠进程退出再重建来落地。别信“reload 就生效”,那是幻觉。


















