无Supervisor的Laravel 10队列Worker托管服务器优化配置
无Root权限下Laravel 10队列Worker的优化方案(Hetzner托管环境)
现有方案与问题
在Hetzner托管服务器(仅SSH权限,无法使用Supervisor)中,当前通过Laravel调度实现队列Worker管理,代码如下:
// app/Console/Kernel.php $schedule->call(function() use ($phpShellCmd) { shell_exec("php ".base_path()."/artisan queue:restart"); file_put_contents(storage_path()."/logs/scheduler.log", "[".date('Y-m-d H:i:s')."] queue:restart executed.\n", FILE_APPEND); })->everyMinute(); for($i = 0; $i < $num_of_workers; $i++) { $schedule->call(function() use ($i, $laravelbasedir) { $cmd = "php ".base_path()."/artisan queue:work --queue=MainQueue,OrderQueue --timeout=60 >> ".storage_path()."/logs/queueworker.log 2>/dev/null >/dev/null &"; file_put_contents(storage_path()."/logs/scheduler.log", "[".date('Y-m-d H:i:s')."] execute queue:work #".($i+1).":".$cmd."\n", FILE_APPEND); shell_exec($cmd); })->everyMinute()->runInBackground(); }
设计思路与问题点
- 设计思路:每分钟优雅重启队列+启动指定数量Worker,实现代码更新自动生效、Worker崩溃自动恢复,通过60秒超时控制进程数量。
- 现存问题:任务实际运行时长超过60秒,多数情况正常但偶尔触发超时错误(提示"任务尝试次数过多或运行过久",无实际异常信息);频繁重启导致正在运行的任务被强制终止,引发不必要的重试;每分钟启动新Worker易造成进程冗余。
优化方案(无需Root/Supervisor)
方案1:进程监控脚本+Laravel调度
取消频繁重启逻辑,改用脚本监控Worker进程数量,仅在进程不足时补充。
实现步骤
- 创建Shell监控脚本
queue-monitor.sh:
#!/bin/bash NUM_WORKERS=3 BASE_DIR=/path/to/your/laravel LOG_FILE=$BASE_DIR/storage/logs/queueworker.log SCHEDULER_LOG=$BASE_DIR/storage/logs/scheduler.log # 统计当前活跃的queue:work进程数 CURRENT_WORKERS=$(ps aux | grep "php artisan queue:work" | grep -v grep | wc -l) # 补充缺失的Worker while [ $CURRENT_WORKERS -lt $NUM_WORKERS ]; do nohup php $BASE_DIR/artisan queue:work --queue=MainQueue,OrderQueue --timeout=180 --tries=3 --sleep=5 --max-jobs=100 >> $LOG_FILE 2>&1 & CURRENT_WORKERS=$((CURRENT_WORKERS + 1)) echo "[$(date '+Y-m-d H:i:s')] Started new worker. Total: $CURRENT_WORKERS" >> $SCHEDULER_LOG done
- 在Laravel调度中添加脚本调用:
// app/Console/Kernel.php $schedule->exec(base_path('queue-monitor.sh'))->everyMinute()->runInBackground();
- 给脚本添加执行权限:
chmod +x queue-monitor.sh
优缺点
- 优点:减少频繁重启的资源消耗,Worker可处理指定数量任务后再退出(
--max-jobs);超时时间可匹配实际任务时长,避免误杀正常任务。 - 缺点:需维护Shell脚本,进程统计依赖
ps命令输出,兼容性有限;代码更新后需手动执行php artisan queue:restart。
方案2:Laravel调度内进程校验
直接在Laravel调度中检查Worker进程状态,仅启动缺失的Worker。
实现代码
// app/Console/Kernel.php $schedule->call(function () use ($num_of_workers) { $basePath = base_path(); $logPath = storage_path('logs/queueworker.log'); $schedulerLog = storage_path('logs/scheduler.log'); for ($i = 0; $i < $num_of_workers; $i++) { // 通过Worker ID校验进程是否存在 $processCheck = shell_exec("ps aux | grep 'queue:work --queue=MainQueue,OrderQueue --worker-id=$i' | grep -v grep"); if (empty($processCheck)) { $cmd = "nohup php $basePath/artisan queue:work --queue=MainQueue,OrderQueue --timeout=180 --tries=3 --sleep=5 --worker-id=$i >> $logPath 2>&1 &"; shell_exec($cmd); file_put_contents($schedulerLog, "[" . date('Y-m-d H:i:s') . "] Started missing worker #$i: $cmd\n", FILE_APPEND); } } })->everyMinute()->runInBackground();
优缺点
- 优点:无需额外Shell脚本,直接通过Laravel调度管理;
--worker-id标识避免重复启动进程。 - 缺点:进程校验依赖
ps命令输出,健壮性不足;代码更新后仍需手动触发队列重启。
方案3:用户级systemd服务(推荐)
Hetzner服务器支持用户级systemd服务(无需Root权限),可实现类似Supervisor的进程管理。
实现步骤
- 创建systemd服务文件
~/.config/systemd/user/laravel-queue@.service:
[Unit] Description=Laravel Queue Worker %I After=network.target [Service] WorkingDirectory=/path/to/your/laravel ExecStart=/usr/bin/php artisan queue:work --queue=MainQueue,OrderQueue --timeout=180 --tries=3 --sleep=5 Restart=always RestartSec=10 User=your-ssh-username StandardOutput=append:/path/to/your/laravel/storage/logs/queueworker-%I.log StandardError=append:/path/to/your/laravel/storage/logs/queueworker-%I.log [Install] WantedBy=default.target
- 启用并启动多个Worker实例:
systemctl --user daemon-reload # 启动3个Worker示例 for i in {1..3}; do systemctl --user enable --now laravel-queue@$i.service done
- 常用管理命令:
# 查看单个Worker状态 systemctl --user status laravel-queue@1.service # 重启所有Worker systemctl --user restart laravel-queue@*.service # 实时查看日志 journalctl --user -u laravel-queue@1.service -f
优缺点
- 优点:最接近Supervisor的稳定体验,自动重启崩溃Worker;日志管理清晰,代码更新后执行
php artisan queue:restart即可优雅重启所有Worker;无需依赖Laravel调度,资源消耗更低。 - 缺点:需了解systemd基础配置;极少数共享托管环境可能限制用户级systemd服务,但Hetzner VPS/独立服务器均支持。
问题根源总结
现有方案的核心问题:
- 每分钟
queue:restart强制终止正在运行的Worker,导致未完成任务被标记为超时并触发重试。 - 每分钟启动新Worker,易造成进程冗余,增加服务器负载。
--timeout=60设置的是Worker最大运行时长,而非任务超时,强制终止长耗时任务引发错误。
内容的提问来源于stack exchange,提问作者alexandre
相关产品推荐
相关产品推荐

