剖析Laravel隊列系統--推送做業到隊列

原文連接https://divinglaravel.com/queue-system/pushing-jobs-to-queuephp

There are several ways to push jobs into the queue:
有幾種方法能夠將做業推送到隊列中:laravel

Queue::push(new InvoiceEmail($order));

Bus::dispatch(new InvoiceEmail($order));

dispatch(new InvoiceEmail($order));

(new InvoiceEmail($order))->dispatch();

As explained in a previous dive, calls on the Queue facade are calls on the queue driver your app uses, calling the push method for example is a call to the push method of the Queue\DatabaseQueue class in case you're using the database queue driver.數據庫

There are several useful methods you can use:閉包

調用Queue facade是對應用程序使用的隊列驅動的調用,若是你使用數據庫隊列驅動,調用push方法是調用Queue\DatabaseQueue類的push方法。app

有幾種有用的方法能夠使用:ide

// 將做業推送到特定的隊列
Queue::pushOn('emails', new InvoiceEmail($order));

// 在給定的秒數以後推送做業
Queue::later(60, new InvoiceEmail($order));

// 延遲後將做業推送到特定的隊列
Queue::laterOn('emails', 60, new InvoiceEmail($order));

// 推送多個做業
Queue::bulk([
    new InvoiceEmail($order),
    new ThankYouEmail($order)
]);

// 推送特定隊列上的多個做業
Queue::bulk([
    new InvoiceEmail($order),
    new ThankYouEmail($order)
], null, 'emails');

After calling any of these methods, the selected queue driver will store the given information in a storage space for workers to pick up on demand.函數

調用這些方法以後,所選擇的隊列驅動會將給定的信息存儲在存儲空間中,供workers按需獲取。ui

Using the command bus

使用命令總線

Dispatching jobs to queue using the command bus gives you extra control; you can set the selected connection, queue, and delay from within your job class, decide if the command should be queued or run instantly, send the job through a pipeline before running it, actually you can even handle the whole queueing process from within your job class.this

The Bus facade proxies to the Contracts\Bus\Dispatcher container alias, this alias is resolved into an instance of Bus\Dispatcher inside Bus\BusServiceProvider:spa

使用命令總線調度做業進行排隊能夠給你額外控制權; 您能夠從做業類中設置選定的connection, queue, and delay 來決定命令是否應該排隊或當即運行,在運行以前經過管道發送做業,實際上你甚至能夠從你的做業類中處理整個隊列過程。

Bug facade代理到 Contracts\Bus\Dispatcher 容器別名,此別名解析爲Bus\Dispatcher內的Bus\BusServiceProvider的一個實例:

$this->app->singleton(Dispatcher::class, function ($app) {
    return new Dispatcher($app, function ($connection = null) use ($app) {
        return $app[QueueFactoryContract::class]->connection($connection);
    });
});

因此Bus::dispatch() 調用的 dispatch() 方法是 Bus\Dispatcher 類的:

public function dispatch($command)
{
    if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
        return $this->dispatchToQueue($command);
    } else {
        return $this->dispatchNow($command);
    }
}

This method decides if the given job should be dispatched to queue or run instantly, the commandShouldBeQueued() method checks if the job class is an instance of Contracts\Queue\ShouldQueue, so using this method your job will only be queued in case you implement the ShouldQueue interface.

We're not going to look into dispatchNow in this dive, it'll be discussed in detail when we dive into how workers run jobs. For now let's look into how the Bus dispatches your job to queue:

該方法決定是否將給定的做業分派到隊列或當即運行,commandShouldBeQueued() 方法檢查做業類是不是 Contracts\Queue\ShouldQueue, 的實例,所以使用此方法,您的做業只有繼承了ShouldQueue接口才會被放到隊列中。

咱們不會在這篇中深刻dispatchNow,咱們將在深刻worker如何執行做業中詳細討論。 如今咱們來看看總線如何調度你的工做隊列:

public function dispatchToQueue($command)
{
    $connection = isset($command->connection) ? $command->connection : null;

    $queue = call_user_func($this->queueResolver, $connection);

    if (! $queue instanceof Queue) {
        throw new RuntimeException('Queue resolver did not return a Queue implementation.');
    }

    if (method_exists($command, 'queue')) {
        return $command->queue($queue, $command);
    } else {
        return $this->pushCommandToQueue($queue, $command);
    }
}

First Laravel checks if a connection property is defined in your job class, using the property you can set which connection Laravel should send the queued job to, if no property was defined null will be used and in such case Laravel will use the default connection.

Using the connection value, Laravel uses a queueResolver closure to build the instance of the queue driver that should be used, this closure is set inside Bus\BusServiceProvider while registering the Dispatcher instance:

首先 Laravel會檢查您的做業類中是否認義了connection 屬性,使用這個屬性能夠設置Laravel應該將排隊做業發送到哪一個鏈接,若是未定義任何屬性,將使用null屬性,在這種狀況下Laravel將使用默認鏈接。

經過設置的鏈接,Laravel使用一個queueResolver閉包來構建應該使用哪一個隊列驅動的實例,當註冊調度器實例的時候這個閉包在Bus\BusServiceProvider 中被設置:

function ($connection = null) use ($app) {
    return $app[Contracts\Queue\Factory::class]->connection($connection);
}

Contracts\Queue\Factory is an alias for Queue\QueueManager, so in other words this closure returns an instance of QueueManager and sets the desired connection for the manager to know which driver to use.

Finally the dispatchToQueue method checks if the job class has a queue method, if that's the case the dispatcher will just call this method giving you full control over how the job should be queued, you can select the queue, assign delay, set maximum retries, timeout, etc...

In case no queue method was found, a call to pushCommandToQueue() calls the proper pushmethod on the selected queue driver:

Contracts\Queue\FactoryQueue\QueueManager的別名,換句話說,該閉包返回一個QueueManager實例,併爲manager設置所使用的隊列驅動須要的鏈接。

最後,dispatchToQueue方法檢查做業類是否具備queue方法,若是調度器調用此方法,能夠徹底控制做業排隊的方式,您能夠選擇隊列,分配延遲,設置最大重試次數, 超時等

若是沒有找到 queue 方法,對 pushCommandToQueue() 的調用將調用所選隊列驅動上的push方法:

protected function pushCommandToQueue($queue, $command)
{
    if (isset($command->queue, $command->delay)) {
        return $queue->laterOn($command->queue, $command->delay, $command);
    }

    if (isset($command->queue)) {
        return $queue->pushOn($command->queue, $command);
    }

    if (isset($command->delay)) {
        return $queue->later($command->delay, $command);
    }

    return $queue->push($command);
}

The dispatcher checks for queue and delay properties in your Job class & picks the appropriate queue method based on that.

調度器檢查Job類中的 queuedelay ,並根據此選擇適當的隊列方法。

So I can set the queue, delay, and connection inside the job class?

因此我能夠設置工做類中的隊列,延遲和鏈接?

Yes, you can also set a tries and timeout properties and the queue driver will use these values as well, here's how your job class might look like:

是的,您還能夠設置一個triestimeout 屬性,隊列驅動也將使用這些值,如下工做類示例:

class SendInvoiceEmail{
    public $connection = 'default';

    public $queue = 'emails';

    public $delay = 60;

    public $tries = 3;

    public $timeout = 20;
}

Setting job configuration on the fly

即時設置做業配置

Using the dispatch() global helper you can do something like this:

使用 dispatch() 全局幫助方法,您能夠執行如下操做:

dispatch(new InvoiceEmail($order))
        ->onConnection('default')
        ->onQueue('emails')
        ->delay(60);

This only works if you use the Bus\Queueable trait in your job class, this trait contains several methods that you may use to set some properties on the job class before dispatching it, for example:

這隻有在您在做業類中使用 Bus\Queueable trait時纔有效,此trait包含幾種方法,您能夠在分發做業類以前在做業類上設置一些屬性,例如:

public function onQueue($queue)
{
    $this->queue = $queue;

    return $this;
}

But in your example we call the methods on the return of dispatch()!

可是在你的例子中,咱們調用dispatch()的返回方法!

Here's the trick:

這是訣竅:

function dispatch($job)
{
    return new PendingDispatch($job);
}

This is the definition of the dispatch() helper in Foundation/helpers.php, it returns an instance of Bus\PendingDispatch and inside this class we have methods like this:

這是在Foundation/helpers.php中的dispatch()幫助方法的定義,它返回一個Bus\PendingDispatch 的實例,而且在這個類中,咱們有這樣的方法:

public function onQueue($queue)
{
    $this->job->onQueue($queue);

    return $this;
}

So when we do dispatch(new JobClass())->onQueue('default'), the onQueue method of PendingDispatch will call the onQueue method on the job class, as we mentioned earlier job classes need to use the Queueable trait for all this to work.

因此當咱們執行 dispatch(new JobClass())->onQueue('default'), 時,PendingDispatchonQueue 方法將調用job類上的 onQueue 方法,如前所述,做業類須要使用全部這些的 Queueable trait來工做。

Then where's the part where the Dispatcher::dispatch method is called?

那麼調用Dispatcher::dispatch方法的那部分是哪裏?

Once you do dispatch(new JobClass())->onQueue('default') you'll have the job instance ready for dispatching, the actual work happens inside PendingDispatch::__destruct():

一旦你執行了 dispatch(new JobClass())->onQueue('default'),你將讓做業實例準備好進行調度,實際的工做發生在 PendingDispatch::__destruct()中:

public function __destruct()
{
    app(Dispatcher::class)->dispatch($this->job);
}

This method, when called, will resolve an instance of Dispatcher from the container and call the dispatch() method on it. A __destruct() is a PHP magic method that's called when all references to the object no longer exist or when the script terminates, and since we don't store a reference to the PendingDispatch instance anywhere the __destruct method will be called immediately:

調用此方法時,將從容器中解析 Dispatcher 的一個實例,而後調用它的dispatch()方法。 destruct()是一種PHP魔術方法,當對對象的全部引用再也不存在或腳本終止時,都會調用,由於咱們不會當即在 __destruct方法中存儲對PendingDispatch 實例的引用,

// Here the destructor will be called rightaway
dispatch(new JobClass())->onQueue('default');

// 若是咱們調用unset($temporaryVariable),那麼析構函數將被調用
// 或腳本完成執行時。
$temporaryVariable = dispatch(new JobClass())->onQueue('default');

Using the Dispatchable trait

使用可調度的特徵

You can use the Bus\Dispatchable trait on your job class to be able to dispatch your jobs like this:

您能夠使用工做類上的 Bus\Dispatchable trait來調度您的工做,以下所示:

(new InvoiceEmail($order))->dispatch();

Here's how the dispatch method of Dispatchable looks like:

調度方法 Dispatchable看起來像這樣:

public static function dispatch()
{
    return new PendingDispatch(new static(...func_get_args()));
}

As you can see it uses an instance of PendingDispatch, that means we can do something like:

正如你能夠看到它使用一個 PendingDispatch的實例,這意味着咱們能夠作一些像這樣的事:

(new InvoiceEmail($order))->dispatch()->onQueue('emails');

轉載請註明: 轉載自Ryan是菜鳥 | LNMP技術棧筆記

若是以爲本篇文章對您十分有益,何不 打賞一下

謝謝打賞

本文連接地址: 剖析Laravel隊列系統--推送做業到隊列

相關文章
相關標籤/搜索