【问题标题】:Laravel Queues: How to process one set of jobs only after another set of jobs are processed?Laravel Queues:如何在处理完另一组作业后才处理一组作业?
【发布时间】:2019-03-12 20:53:06
【问题描述】:

我有两份工作:

  • Set1: 工作 A、工作 B、工作 C
  • 第 2 组: 工作 D、工作 E、工作 F

如何处理Set1中的所有作业,然后处理Set2中的作业?

我尝试过工作链。但是,如果之前的作业失败,作业链接将停止处理作业。即使其中一个失败了,我也想全部处理。

它应该处理Set1(A,B,C)中的所有作业,如果作业A的进程失败,B和C的进程不应停止。 当 Set1 的所有作业都处理完后(无论是失败还是成功),都应该拿起 Set2 中的作业来处理。

这里的作业顺序不是优先级。它们可以按任何顺序处理。
唯一的规则是 Set2 中的作业只有在 Set1 中的所有作业都处理完后才能处理。

我无法使用延迟调度,因为处理 Set1 作业所需的时间差异很大。

【问题讨论】:

    标签: php laravel


    【解决方案1】:

    现在可以在 Laravel 8.x 中使用 Job Batching 来完成:

    $batch = Bus::batch([
        new ImportCsv(1, 100),
        new ImportCsv(101, 200),
        new ImportCsv(201, 300),
        new ImportCsv(301, 400),
        new ImportCsv(401, 500),
    ])->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();
    

    【讨论】:

      【解决方案2】:

      Laravel 中的作业链接基于这样一个事实,即如果当前作业失败,它将不会执行下一个作业。

      您可以使用事件。每个作业有两种方法handle() 和failed()。您可以在作业成功执行或失败时发出事件。请参见下面的示例:

      例如:

      <?php
      
      namespace App\Jobs;
      
      use Exception;
      use App\Something;
      use Illuminate\Bus\Queueable;
      use Illuminate\Queue\SerializesModels;
      use Illuminate\Queue\InteractsWithQueue;
      use Illuminate\Contracts\Queue\ShouldQueue;
      
      class JobA implements ShouldQueue
      {
          use InteractsWithQueue, Queueable, SerializesModels;
      
          protected $something;
      
          /**
           * Create a new job instance.
           *
           * @param  Something $somethin
           * @return void
           */
          public function __construct(Something $something)
          {
              $this->something = $something;
          }
      
          /**
           * Execute the job.
           *
           * @param  AudioProcessor  $processor
           * @return void
           */
          public function handle()
          {
              // Execute Job on something
              event(new JobAHandled($something));
          }
      
          /**
           * The job failed to process.
           *
           * @param  Exception  $exception
           * @return void
           */
          public function failed(Exception $exception)
          {
              // Send user notification of failure, etc...
              event(new JobAFailed($something, $e));
          }
      }
      

      现在,您可以通过监听事件 JobAHandled 和 JobAFailed 来调用下一个作业。这将使您能够控制,即使您继续执行下一个作业,您也知道之前的作业失败了。如果需要,您可以记录下来。

      然后你可以对其他工作做同样的事情,而不是它所在的集合。

      还要确保如果您尝试该作业,那么将再次执行进一步的作业,因为考虑到您的情况,失败的事件也将执行下一个作业。

      【讨论】:

      • 这将为特定作业发出事件。我如何知道特定集合中的所有作业都已处理?我想要实现的是仅在处理作业 A、B、C 之后处理作业 D、E、F。
      • @SrinathReddyDudi 你有没有找到解决这个问题的方法?
      • @Isaiahiroko 不。我能做到的最接近的方法是延迟处理第 2 组作业,其中第 1 组作业需要完成的最长时间。
      • 感谢@SrinathReddyDudi。最终可能会回到那个状态。
      【解决方案3】:

      一个想法是创建一个 JobSet 作业和一个 JobWorker 作业。

      因此,作业集将您的新 JobWorker("A")、JobWorker("B")、JobWorker("C") 作为参数,然后调度所有这些。

      dispatch(new JobSet(new JobWorker("A"), new JobWorker("B"), new JobWorker("C")));
      

      以及“JobSet”工作的灵感

      class JobSet implements ShouldQueue
      {
          use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      
          private $a;
          private $b;
          private $c;
      
          /**
           * Create a new job instance.
           *
           * @return void
           */
          public function __construct(JobWorker $a, JobWorker $b, JobWorker $c)
          {
              $this->a = $a;
              $this->b = $b;
              $this->c = $c;
          }
      
          /**
           * Execute the job.
           *
           * @return void
           */
          public function handle()
          {
              dispatch($this->a);
              dispatch($this->b);
              dispatch($this->c);
          }
      }
      

      【讨论】:

      • 我认为这是为了分派一组工作。我的问题是,只有在上一组中的所有作业都处理完之后,如何处理特定的一组作业?
      【解决方案4】:

      您可以将它们分派到不同的队列:

      A::dispatch()->onQueue('set1');
      B::dispatch()->onQueue('set1');
      C::dispatch()->onQueue('set1');
      
      D::dispatch()->onQueue('set2');
      E::dispatch()->onQueue('set2');
      F::dispatch()->onQueue('set2');
      

      并在检查 set2 之前运行确保 set1 为空的工作程序:

      php artisan queue:work --queue=set1,set2
      

      要启动一个工作程序,在继续处理low 队列上的任何作业之前验证所有high 队列作业是否已处理,请将一个以逗号分隔的队列名称列表传递给work 命令:

      php artisan queue:work --queue=high,low
      

      https://laravel.com/docs/5.8/queues#running-the-queue-worker

      编辑:

      您可以通过启动更多工作人员来解决来自 User2 的 set1 作业延迟来自 User1 的 set2 作业。

      【讨论】:

      • 这仅适用于每组的一个实例。例如,如果有 10 个用户并且他们分派了不同的 set1 和 set2 实例,那么只有当 set1 队列为空时,才会处理 set2 的作业。这意味着 set2 中的作业应该等到 set1 中的所有作业都处理完。这使得每个用户的 set2 队列中的每个作业都等待,直到 set1 队列中的所有作业都处理完。我希望他们在处理完 set1 队列中属于该用户的所有作业后立即处理这些作业。直到 set1 队列中的所有作业都处理完。
      • @SrinathReddyDudi 它可能有助于更新问题,因为没有提到每个用户的队列。如果我对您的理解正确,那么我无法想象您为什么要那样做;为什么需要以这种方式工作的具体示例可能会有所帮助。如果允许处理特定用户的 set2 作业而不管他们的 set1 作业是否成功,那么为什么顺序很重要?如果添加 set1 作业的其他用户延迟了 set2 作业的处理,您不能简单地启动更多的工作人员,以便队列不会一直满吗?
      猜你喜欢
      • 2016-10-17
      • 1970-01-01
      • 2016-04-16
      • 2020-10-31
      • 1970-01-01
      • 2014-12-21
      • 2022-11-01
      • 1970-01-01
      • 2020-08-07
      相关资源
      最近更新 更多