背景 supervisorctl restart 是否会导致队列内消息消费异常。使用supervisorctl + laravel horizon。
这条命令 supervisorctl restart queue 的执行逻辑涉及两个层面:Supervisor 进程管理层面 和 Laravel 应用程序(PHP 进程)层面。
Supervisor 层面(进程调度)
当你在终端执行 supervisorctl restart queue 时:
客户端通信:supervisorctl(客户端)通过 XML-RPC 接口(通常是 Unix Socket 或 TCP 端口)向正在运行的 supervisord(守护进程)发送 restart 指令。
发送停止信号:
- supervisord 查找名为 queue 的进程(或组)。
- 它向该进程发送停止信号。默认通常是 SIGTERM(终止信号),配置中可以通过 stopsignal 参数修改(Laravel 推荐使用 SIGTERM 或 SIGINT)。
等待退出:supervisord 等待该进程完全退出(状态变为 STOPPED)。如果进程在 stopwaitsecs(在配置文件中由 stopwaitsecs参数指定,例如默认60秒)内没有退出,Supervisor 可能会发送 SIGKILL 强制杀死它。
重新启动:一旦进程退出,supervisord 根据配置文件中的 command 指令(php artisan horizon)重新启动一个新的进程。
状态更新:新进程启动成功后,状态变为 RUNNING。
Laravel 应用层面(内部逻辑)
接收信号:php artisan horizon 主进程接收到 SIGTERM 信号。
Horizon 终止流程:
- Horizon 主进程会向它管理的所有 子进程(实际处理任务的 worker)发送终止指令。
- Horizon 会等待当前的 Job 处理完毕(Graceful Shutdown)。
- Horizon 自身停止运行。
重启:Supervisor 启动新的 php artisan horizon 进程。
初始化:新的 Horizon 进程读取 config/horizon.php,重新扫描 Redis 中的指标,并根据配置启动一批新的 Worker 子进程来处理队列。
测试
准备消费者逻辑
public function handle(): void
{
sleep(10);
DB::table('test')->insert([
'time' => Carbon::now()->toDateTimeString(),
]);
}
准备只开启一个消费者
927 33359 33359 927 ? -1 S 0 1:23 \_ /usr/bin/php8.3 artisan horizon
33359 33655 33359 927 ? -1 S 0 1:09 | \_ /usr/bin/php8.3 artisan horizon:supervisor zulin-test-FVdo:supervisor-1 redis --workers-name=defau
33655 33708 33359 927 ? -1 S 0 0:28 | \_ /usr/bin/php8.3 artisan horizon:work redis --name=default --supervisor=zulin-test-FVdo:supervi
验证步骤 先向队列中推送三条消息 2-3S后 重启队列 观察 test 表中的数据分布
4 2025-12-02 15:51:15
5 2025-12-02 15:51:31
6 2025-12-02 15:51:41
结论
基本不会影响 除非 任务的执行时间 大于 supervisord stopwaitsecs 的设置时间
111
真滴强