Laravel 8中如何在Job内调度Job?订单自动派单异常求助
业务场景
订单下达后,若司机60秒内未响应,需自动派给下一位司机,因此需在Laravel Job中调度另一个Job。
控制器index方法代码
public function index() { $order = Order::create([ 'name' => "order1", "driver_id" => "1", "status" => "0", "price" => "500" ]); dispatch(new OrderPlacedJob($order))->delay(now()->addSecond(30)); }
OrderPlacedJob类代码
class OrderPlacedJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $order; public function __construct(Order $order) { $this->order = $order; } public function handle() { $order = $this->order; if ($this->order->status == '0') { $driver_id = $this->order->driver_id + 1; $this->order->driver_id = $driver_id; $this->order->save(); } OrderPlacedJob::dispatch($order)->delay(now()->addSecond(30)); } }
问题
代码首次执行正常,给ID为1的司机派单后,若司机未响应,driver_id更新为2,但后续任务不再执行,求解决办法。
解决办法
问题原因分析
- 模型序列化可能导致获取旧数据:虽然
SerializesModels会在Job执行时重新加载模型,但直接使用$this->order仍可能存在数据不一致的情况。 - 无限调度Job引发队列问题:原代码无论订单状态如何都会持续创建新Job,可能导致队列堆积、Worker内存溢出或崩溃。
- 字段类型不匹配:
driver_id在数据库中若为字符串类型,与整数运算可能引发隐性错误,导致Job失败。
具体修复步骤
1. 刷新模型并增加终止条件
修改OrderPlacedJob的handle方法,主动获取最新订单数据,并仅在订单未响应时继续调度Job:
public function handle() { // 从数据库加载最新订单信息,避免使用序列化的旧实例 $order = $this->order->fresh(); // 订单不存在时直接退出 if (!$order) { return; } if ($order->status == '0') { // 显式转换driver_id为整数,避免类型错误 $driverId = (int)$order->driver_id + 1; $order->driver_id = $driverId; $order->save(); } // 仅当订单仍未响应时,才调度下一个任务 if ($order->status == '0') { OrderPlacedJob::dispatch($order)->delay(now()->addSeconds(30)); } }
2. 统一字段类型
检查数据库中driver_id字段类型,建议设置为INT类型。若必须使用字符串,修改运算逻辑:
$driverId = (string)((int)$order->driver_id + 1);
3. 检查队列Worker配置
- 确保Worker持续运行:使用
php artisan queue:work启动守护进程模式的Worker(Laravel 9+),或php artisan queue:work --daemon(Laravel 8及以下)。 - 查看失败任务:执行
php artisan queue:failed查看是否有失败的Job,根据错误信息排查问题。 - 设置Job重试次数:在
OrderPlacedJob类中添加重试配置,避免单次失败导致任务终止:
class OrderPlacedJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // 设置最多重试5次 public $tries = 5; // ... 其他代码 }
4. 优化调度逻辑(可选)
如果无限循环调度Job不符合需求,可以改用Laravel任务调度定期检查未响应订单:
// app/Console/Kernel.php protected function schedule(Schedule $schedule) { $schedule->call(function () { // 筛选出未响应且超过30秒的订单(根据需求调整时间) Order::where('status', '0') ->where('created_at', '<=', now()->subSeconds(30)) ->each(function ($order) { $driverId = (int)$order->driver_id + 1; $order->driver_id = $driverId; $order->save(); }); })->everyThirtySeconds(); }
内容的提问来源于stack exchange,提问作者Zaeem Syed
相关产品推荐
相关产品推荐

