Laravel嵌套工作



我创建了一个作业,该作业具有一个foreach循环,可派遣另一个作业。即使所有嵌套工作都完成,有没有办法开火?

当触发时,这里发生了什么

步骤1.首先,我触发批处理作业 GenerateBatchReports::dispatch($orderable);

步骤2。然后,我们运行一个循环并排队其他作业

/**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $dir = storage_path('reports/tmp/'.str_slug($this->event->company) . '-event');
        if(file_exists($dir)) {
            File::deleteDirectory($dir);
        }
        foreach($this->event->participants as $participant) {
            $model = $participant->exercise;
            GenerateSingleReport::dispatch($model);
        }
    }

我只需要知道何时完成所有嵌套作业,以便我可以将报告汇总并通过电子邮件发送给用户。当批处理作业排队所有嵌套作业时,将其从列表中删除。是否有一种方法可以保持工作,直到完成嵌套工作,然后开火?

任何帮助将不胜感激。

update :Laravel 8(计划于2020年9月8日发布(将提供作业批处理。此功能已经记录在记录的情况下可能非常适合嵌套作业方案,看起来像这样:

$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);
        }
    }
}

或者

我正在提出另一个想法,因此代码并非完美无瑕。也许可以创建包装作业,并致力于运行最后的工作封锁的最后嵌套作业。

/**
     * 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();
    }
}

for laravel> = 5.7

您可以使用dispatchNow方法。这将使父母的工作在处理时保持生命:

https://laravel.com/docs/5.8/queues#synchronous-dispatching

父级作业:

public function handle()
{
    // ...
    foreach($this->event->participants as $participant) {
        $model = $participant->exercise;
        GenerateSingleReport::dispatchNow($model);
    }
    // then do something else...
}

对于Laravel 5.2-5.6

您可以使用sync连接:

https://laravel.com/docs/5.5/queues#customizing-the-queue-and-connection

确保连接是在您的config/queue.php中定义的:

https://github.com/laravel/laravel/laravel/blob/5.5/config/queue.php#l31

父级作业(注意:此语法适用于5.5。5.2的文档有点不同(:

public function handle()
{
    // ...
    foreach($this->event->participants as $participant) {
        $model = $participant->exercise;
        GenerateSingleReport::dispatch($model)->onConnection('sync');
    }
    // then do something else...
}

您可以使用Laravel的工作链。它允许您按顺序运行一堆作业,如果失败,将不会运行链中的其余作业。

基本语法看起来像这样:

FirstJob::withChain([
    new SecondJob($param),
    new ThirdJob($param)
])->dispatch($param_for_first_job);

在您的情况下,您可以将所有GenerateSingleReport作业添加到一个数组中,除第一个作业外,然后添加要运行的最终作业。然后,您可以将该数组传递到第一个作业的withChain方法。

$jobs = [];
$first_job = null;
$first_parameter = null;
foreach($this->event->participants as $participant) {
    $model = $participant->exercise;
    if (empty($first_job)) {
        $first_job = GenerateSingleReport;
        $first_parameter = $model;
    } else {
        $jobs[] = new GenerateSingleReport($model);
    }            
}
$jobs[] = new FinalJob();
$first_job->withChain($jobs)->dispatch($first_parameter);

最新更新