Laravel如何使用Job Batching任务批处理_Laravel多个异步任务进度追踪【方法】

17次阅读

Job batching 是 laravel 8.5+ 原生队列批处理机制,需 database/redis 驱动,通过 Bus::batch() 创建含 then/catch/finally 回调的 Batch 实例并 dispatch() 异步执行,状态由 job_batches 表自动维护,前端应通过 BatchRepository 查询进度。

Laravel如何使用Job Batching任务批处理_Laravel多个异步任务进度追踪【方法】

Job Batching 是 Laravel 8.5+ 原生支持的特性,不是插件或第三方包

如果你还在用 dispatch() 逐个推任务、再靠数据库字段手动记进度,说明你没启用 Laravel 自带的批处理机制。Job Batching 要求最低 Laravel 版本为 8.5,且必须使用 databaseredis 作为队列驱动(sync 不支持)。它本质是把一批 Job 封装成一个 Batch 实例,由框架统一追踪完成数、失败数、取消状态等元数据。

如何创建并分发一个 Batch(含实际参数控制)

不要用 Bus::batch() 后直接 dispatch() —— 这会立刻执行,失去异步意义。正确流程是先构建 Batch,再调用 dispatch() 推入队列:

use IlluminateSupportFacadesBus;  $batch = Bus::batch([     new SendNotificationJob($user1),     new SendNotificationJob($user2),     new SendNotificationJob($user3), ])->then(function (Batch $batch) {     Log::info('所有通知发送完成', ['batch_id' => $batch->id]); })->catch(function (Batch $batch, Throwable $e) {     Log::error('批处理中发生错误', ['batch_id' => $batch->id, 'error' => $e->getMessage()]); })->finally(function (Batch $batch) {     Log::info('无论成功失败都会执行', ['batch_id' => $batch->id]); })->dispatch();  // 注意:此时 $batch->id 已生成,但尚未开始执行 // 可立即用于前端轮询或存入 session
  • Bus::batch([...]) 接收 Job 实例数组,每个 Job 必须实现 ShouldQueue
  • then()/catch()/finally() 回调在「整个 Batch 生命周期结束时」触发,运行在单独的 Job 中(需确保该 Job 也能被正常消费)
  • 默认超时为 24 小时,可通过 timeout() 方法修改:->timeout(3600)

前端如何实时查 Batch 进度(关键字段和查询方式)

Batch 状态不依赖你自建表,Laravel 在 job_batches 表中自动维护。最常用字段是:total_jobspending_jobsfailed_jobsfinished_jobsfailed_job_idsjsON 字符串)、options(含 timeout 等)。查询示例:

// 控制器中提供进度接口 public function batchStatus(string $batchId) {     $batch = IlluminateBusBatchRepository::make()->find($batchId);      if (! $batch) {         return response(['message' => 'Batch not found'], 404);     }      return response([         'id' => $batch->id,         'name' => $batch->name ?? 'unnamed',         'progress' => $batch->progress(), // 自动计算百分比(0–100)         'status' => $batch->status(),      // 'pending'|'running'|'finished'|'cancelled'|'failed'         'total' => $batch->totalJobs,         'processed' => $batch->finishedJobs + $batch->failedJobs,         'failed' => $batch->failedJobs,         'cancelled' => $batch->cancelledJobs,     ]); }
  • $batch->progress() 是安全的,即使部分 Job 还没被消费,也会按已知总数估算(例如 5/10 → 50%)
  • 不要用 DB::table('job_batches')->where(...)->first() 直接查——可能读到未刷新的缓存或脏数据;务必走 BatchRepository
  • 前端轮询建议间隔 ≥ 2 秒,避免压垮队列驱动(尤其是 Redis)

常见失败场景和绕过坑点

Batch 失败不等于所有 Job 都失败,但某些配置会让整个 Batch 卡死:

  • 某个 Job 抛出未被捕获的异常,且没定义 catch() 回调 → Batch 状态变为 failed,剩余 pending Job 不再执行
  • Job 内部调用 sleep() 或阻塞 I/O(如同步 http 请求)→ 可能导致单个 Job 超时,进而触发 failed,但 job_batches.failed_job_ids 只存 ID,不存错误
  • 使用 database 驱动时,若队列 worker 意外退出(如 OOM),Batch 状态可能卡在 running → 需配合 php artisan queue:restart 和定期清理脚本
  • Batch 名称(name())默认为空,调试困难 → 强烈建议显式命名:->name('user-import-batch-'.$importId)

真正难排查的是「Batch 显示 finished,但部分 Job 实际没执行」——这通常是因为 Job 构造函数中传入了不可序列化的对象(如 Eloquent Model 实例),导致反序列化失败、静默丢弃。务必只传 ID 或基础类型。

text=ZqhQzanResources