更新:Laravel 8(计划于 2020 年 9 月 8 日发布)将提供作业批处理。这个功能是already documented 可能非常适合嵌套作业场景,如下所示:
$batch = Bus::batch([
new ProcessPodcast(Podcast::find(1)),
new ProcessPodcast(Podcast::find(2)),
new ProcessPodcast(Podcast::find(3)),
new ProcessPodcast(Podcast::find(4)),
new ProcessPodcast(Podcast::find(5)),
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure detected...
})->finally(function (Batch $batch) {
// The batch has finished executing...
})->dispatch();
我们还将能够即时添加其他批处理作业:
$this->batch()->add(Collection::times(1000, function () {
return new ImportContacts;
}));
原答案?
我想出了一个不同的解决方案,因为我有一个使用多个进程的队列。所以,对我来说:
- 没有
dispatchNow,因为我想保持作业并行运行。
- 有多个进程,我需要确保最后一个嵌套作业不会在最后一个嵌套作业之后运行。因此,简单的链接并不能保证这一点。
所以我满足要求的不优雅的解决方案是分派所有嵌套作业,并在最后一个中分派最终作业,延迟几秒钟,以确保所有其他可能仍在运行的嵌套作业并行将被终止。
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$last_participant_id = $this->event->participants->last()->id;
foreach($this->event->participants as $participant) {
$is_last = $participant->id === $last_participant_id;
GenerateSingleReport::dispatch($model, $is_last);
}
}
在GenerateSingleReport.php
class GenerateSingleReport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $model;
protected $runFinalJob;
public function __construct($model, $run_final_job = false)
{
$this->model = $model;
$this->runFinalJob = $run_final_job;
}
public function handle()
{
// job normal stuff…
if ($this->runFinalJob) {
FinalJob::dispatch()->delay(30);
}
}
}
或者
我提出了另一个想法,因此代码并非完美无缺。也许可以创建一个包装器 Job 并专门用于运行与最终作业链接的最后一个嵌套作业。
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$last_participant_id = $this->event->participants->last()->id;
foreach($this->event->participants as $participant) {
$is_last = $participant->id === $last_participant_id;
if ($is_last) {
ChainWithDelay::dispatch(
new GenerateSingleReport($model), // last nested job
new FinalJob(), // final job
30 // delay
);
} else {
GenerateSingleReport::dispatch($model, $is_last);
}
}
}
在ChainWithDelay.php
class ChainWithDelay implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $job;
protected $finalJob;
protected $delay;
public function __construct($job, $final_job, $delay = 0)
{
$this->job = $job;
$this->finalJob = $final_job;
$this->delay = $delay;
}
public function handle()
{
$this->job
->withChain($this->finalJob->delay($this->delay))
->dispatchNow();
}
}