Laravel 嵌套作业

Jua*_*gel 5 foreach nested 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

我只需要知道所有嵌套作业何时完成,以便我可以压缩报告并将它们通过电子邮件发送给用户。当批处理作业完成对所有嵌套作业的排队后,它将从列表中删除。有没有办法在嵌套作业完成之前保持作业,然后触发事件?

任何帮助,将不胜感激。

med*_*uz' 6

更新:Laravel 8(计划于 2020 年 9 月 8 日发布)将提供作业批处理。此功能已记录在案,可能非常适合嵌套作业场景,如下所示:

\n
$batch = Bus::batch([\n    new ProcessPodcast(Podcast::find(1)),\n    new ProcessPodcast(Podcast::find(2)),\n    new ProcessPodcast(Podcast::find(3)),\n    new ProcessPodcast(Podcast::find(4)),\n    new ProcessPodcast(Podcast::find(5)),\n])->then(function (Batch $batch) {\n    // All jobs completed successfully...\n})->catch(function (Batch $batch, Throwable $e) {\n    // First batch job failure detected...\n})->finally(function (Batch $batch) {\n    // The batch has finished executing...\n})->dispatch();\n
Run Code Online (Sandbox Code Playgroud)\n

我们还可以动态添加额外的批处理作业:

\n
$this->batch()->add(Collection::times(1000, function () {\n    return new ImportContacts;\n}));\n
Run Code Online (Sandbox Code Playgroud)\n

原答案

\n

我想出了一个不同的解决方案,因为我有一个使用多个进程的队列。所以,对我来说:

\n
    \n
  • dispatchNow,因为我想保持作业并行运行。
  • \n
  • 有多个进程,我需要确保最后一个嵌套作业不会在最后一个进程之后运行。因此,简单的链接并不能保证这一点。
  • \n
\n

因此,满足要求的我不太优雅的解决方案是分派所有嵌套作业,并且在最后一个作业中,延迟几秒钟分派最终作业,以确保可能仍在并行运行的所有其他嵌套作业将被终止。

\n
/**\n     * Execute the job.\n     *\n     * @return void\n     */\n    public function handle()\n    {\n        $last_participant_id = $this->event->participants->last()->id;\n\n        foreach($this->event->participants as $participant) {\n            $is_last = $participant->id === $last_participant_id;\n\n            GenerateSingleReport::dispatch($model, $is_last);\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

并在GenerateSingleReport.php

\n
class GenerateSingleReport implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    protected $model;\n    protected $runFinalJob;\n\n    public function __construct($model, $run_final_job = false)\n    {\n        $this->model = $model;\n        $this->runFinalJob = $run_final_job;\n    }\n\n    public function handle()\n    {\n        // job normal stuff\xe2\x80\xa6\n\n        if ($this->runFinalJob) {\n            FinalJob::dispatch()->delay(30);\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者

\n

我\xe2\x80\x99m抛出了另一个想法,所以代码并不是完美无缺的。也许可以创建一个包装作业并专门用于运行与最终作业链接的最后一个嵌套作业。

\n
/**\n     * Execute the job.\n     *\n     * @return void\n     */\n    public function handle()\n    {\n        $last_participant_id = $this->event->participants->last()->id;\n\n        foreach($this->event->participants as $participant) {\n            $is_last = $participant->id === $last_participant_id;\n\n            if ($is_last) {\n                ChainWithDelay::dispatch(\n                    new GenerateSingleReport($model), // last nested job\n                    new FinalJob(), // final job\n                    30 // delay\n                );\n            } else {\n                GenerateSingleReport::dispatch($model, $is_last);\n            }\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

并且在ChainWithDelay.php

\n
class ChainWithDelay implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    protected $job;\n    protected $finalJob;\n    protected $delay;\n\n    public function __construct($job, $final_job, $delay = 0)\n    {\n        $this->job = $job;\n        $this->finalJob = $final_job;\n        $this->delay = $delay;\n    }\n\n    public function handle()\n    {\n        $this->job\n            ->withChain($this->finalJob->delay($this->delay))\n            ->dispatchNow();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


new*_*e02 4

对于 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...
}
Run Code Online (Sandbox Code Playgroud)

适用于 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/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...
}
Run Code Online (Sandbox Code Playgroud)

  • 使用“sync”与使用队列在后台处理作业的想法相矛盾。永远不要在生产环境中这样做。 (2认同)