Laravel - Running Jobs in Sequence

2019-08-04 10:11发布

问题:

I am learning Laravel, working on a project which runs Horizon to learn about jobs. I am stuck at one place where I need to run the same job a few times one after one.

Here is what I am currently doing

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Subscriptions;
class MailController extends Controller
{


    public function sendEmail() {
        Subscriptions::all()
        ->each(function($subscription) {
            SendMailJob::dispatch($subscription);
        });
    }
}

This works fine, except it runs the job's across several workers and not in a guaranteed order. Is there any way to run the jobs one after another?

回答1:

What you are looking for, as you mention in your question, is job chaining.

From the Laravel docs

Job chaining allows you to specify a list of queued jobs that should be run in sequence. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the withChain method on any of your dispatchable jobs:

ProcessPodcast::withChain([
    new OptimizePodcast,
    new ReleasePodcast
])->dispatch();

So in your example above

$mailJobs = Subscriptions::all()
    ->map(function($subscription) {
        return new SendMailJob($subscription);
    });

Job::withChain($mailJobs)->dispatch()

Should give the expected result!

Update

If you do not want to use an initial job to chain from (like shown in the documentation example above) you should be able to make an empty Job class that that has use Dispatchable;. Then you can use my example above



回答2:

Everything depends on how many queue workers you will run.

If you run single queue worker, those jobs will be processed in the order they were queued. However, if you run multiple queue workers, obviously they will be run in same time. This is how queues should work. You add some tasks and they might run in same time in different order.

Of course if you want to make sure there is a pause between those jobs, you could inside each add some sleep() but assuming you are running this in controller (what might be not a good idea because what in case you have million subscriptions) it might be not the best solution.



回答3:

What you need is Job Chaining.

You can read all about it in the Laravel website : Chain

Good luck