Laravel隊(duì)列詳解

1.隊(duì)列的應(yīng)用場景:

PHP在異步編程上的短板是眾所周知的,這也是當(dāng)年P(guān)HP能夠迅速火起來的一個(gè)重要特性,當(dāng)然,這也是Nodejs能夠火起來的一個(gè)相反方向的重要特性(Nodejs的亮點(diǎn)就是高并發(fā)的處理性能,只是安全性嘛,仁者見智了),PHP為保持語言的簡單,設(shè)計(jì)之初不考慮多線程,直至PHP7,有了TSRM這樣的多線程并發(fā)處理機(jī)制,PHP 默認(rèn)依然不支持多線程,如果一定要使用多線程,還需要安裝 pthread 擴(kuò)展。盡管并發(fā)處理這塊一致是PHP的短板,同時(shí),大并發(fā)處理的相關(guān)技術(shù)在PHP領(lǐng)域好像也是小妾生的兒子般不受待見,但是并不代表需求中就不需要這些東西,于是乎就有了并發(fā)的另外一種替代方案,就是我們今天所要瞎掰的,隊(duì)列。

可見,隊(duì)列就是為了在某種程度上替代多線程而設(shè)計(jì)的一種處理并發(fā)的方式,同時(shí),也就具備天生的秉性:異步!用于處理耗時(shí)的工作,比如一個(gè)流程走到一個(gè)地方,要發(fā)送一封郵件通知,那我不能讓整個(gè)程序等到郵件發(fā)完了再繼續(xù)下去,那我就把發(fā)郵件的工作丟到一個(gè)隊(duì)列中去,讓這個(gè)工作慢慢做,我繼續(xù)處理響應(yīng)的邏輯。整個(gè)過程其實(shí)也就這么回事,沒有很困難。

2.laravel中的隊(duì)列的實(shí)現(xiàn)

閑話不扯,現(xiàn)在有了laravel,我們不用自己設(shè)計(jì)隊(duì)列的實(shí)現(xiàn)機(jī)制,我們來看看怎么用就好了。

1)首先,我們要先弄清楚幾個(gè)基本概念,我們先來想想,我們要把一項(xiàng)一項(xiàng)的工作任務(wù)放到隊(duì)列里,最樸素的想法,我們會怎么做?我們是不是先得給任務(wù)起個(gè)名字,然后至少得給它一段執(zhí)行的代碼吧,然后再把這一個(gè)任務(wù)作為一個(gè)整體的數(shù)據(jù)結(jié)構(gòu)放到一個(gè)叫做隊(duì)列的數(shù)據(jù)結(jié)構(gòu)中去,那要把名字和執(zhí)行的代碼關(guān)聯(lián)起來,肯定是用鍵值對的方式最方便了,所以,怎么做呢?最簡單就是用數(shù)組嘍,一個(gè)數(shù)組,然后中間一堆鍵值對,鍵名是任務(wù)名,鍵值是任務(wù)的執(zhí)行代碼。但是數(shù)組有一個(gè)問題,數(shù)組的檢索是一個(gè)完了接一個(gè),我沒法控制它中間執(zhí)行的異步操作???但是它能夠模仿出這些操作模式,所以,在laravel中,就有了一個(gè)驅(qū)動名,叫sync,同步嘛,用來做測試用的。

2)但是,如果執(zhí)行代碼太大,然后任務(wù)太多,怎么辦?都擺到數(shù)組里總是不合適吧?那我們是不是就得考慮把這些鍵值對給它放到數(shù)據(jù)庫里面去?嗯,不錯,這是一種好思路,也有人這么做,同樣的,laravel也提供數(shù)據(jù)庫做隊(duì)列的驅(qū)動,和前面一樣,反正我的目的就是把任務(wù)讓他們排隊(duì)去,只是一個(gè)一個(gè)塞到數(shù)據(jù)庫,然后一個(gè)一個(gè)取出來執(zhí)行。

3)然后,就會有人想到了,我干嘛不采用更專業(yè)的軟件來做這個(gè)事?比如redis?人家天生就是鍵值對處理的,而且和數(shù)據(jù)庫不通,redis的數(shù)據(jù)是在內(nèi)存中的,這樣明顯速度就提上來了,我們會這么想,laravel團(tuán)隊(duì)也會這么想,于是有了redis驅(qū)動。

4)然后,有人想到了這么多方法,自然就有人想專業(yè)提供這些服務(wù)的,于是就有比如亞馬遜這樣的公司提供一系列的產(chǎn)品來支持,就有了SQS這些驅(qū)動,等等等等。。。。

其實(shí)說白了,就是把任務(wù)的鍵名和鍵值儲存起來而已,儲存的媒介用什么都可以,而所謂的隊(duì)列驅(qū)動,就是把儲存在這些媒介中的任務(wù)代碼拿住來按照隊(duì)列的算法進(jìn)行執(zhí)行的一種調(diào)配方式的一段代碼而已。

3.講完了這些,我們來看看怎么實(shí)現(xiàn)

我們以redis為例,來看看laravel怎么做隊(duì)列的:

1)一上來沒說的,安裝redis驅(qū)動:composer require 'predis/predis ~1.0'

當(dāng)然,您得先有redis,如果系統(tǒng)沒裝,apt-get一個(gè),yum一個(gè)隨您啦。

2)接下來,要把任務(wù)放隊(duì)列,我們得先做一個(gè)任務(wù)出來,我們寫個(gè)簡單的提交用戶數(shù)據(jù)的任務(wù)吧:

執(zhí)行: php artisan make:job StoreUser

3)執(zhí)行完上述命令,在App下多一個(gè)job目錄出來,然后StoreUser就在里頭,然后我們就可以寫StoreUser的任務(wù)內(nèi)容了:

<?php

namespace App\Jobs;

use ...

class StoreUser implements ShouldQueue

{

? ? use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

? ? protected $user;

? ? public function __construct(User $user)

{

? ? ? ? $this->user=$user;

? ? }

? ? ? public function handle()

{

? ? ? ? $this->user->name="queue";

? ? ? ? $this->user->email="queue@text.com";

? ? ? ? $this->user->password="slcwd";

? ? ? ? $this->user->save();

? ? }

? ? public function fail()

{

? ? ? ? dd('發(fā)送失敗');

? ? }

}

上面這個(gè)任務(wù)很簡單,就是依賴注入一個(gè)User模型,然后在handle方法里面寫一個(gè)用戶數(shù)據(jù)處理的方法,最后儲存,沒設(shè)么意義,我們只是演示而已。

其實(shí),也可以不用注入模型,比如可以寫一個(gè)服務(wù),然后把服務(wù)注入進(jìn)來,反正是個(gè)類就行。

4)接下來,我們告訴框架,要用哪個(gè)驅(qū)動,到.env下面寫這兩句話:

QUEUE_CONNECTION=redis

REDIS_QUEUE=email

這兩個(gè)配置,是配置所謂的“鏈接名”和“隊(duì)列名”。

所謂鏈接,就是我們要存儲的哪一個(gè)存儲介質(zhì),好比銀行,

隊(duì)列名,就是我們要使用哪一條隊(duì)列。

5)這樣,如果使用的是單隊(duì)列,其他就啥都不用管了,這下隊(duì)列已經(jīng)可以用了。文章最后我們再來探討一個(gè)鏈接多個(gè)隊(duì)列的情況。

6)然后我們寫一個(gè)路由,一個(gè)控制器:

路由:Route::get('/mail','Mail\UserQueueController@queue');

控制器的方法代碼:

public function queue()

{

? ? $user=User::find(1);

? ? Test::dispatch($user)->delay(10);

}

這樣,我們就可以把任務(wù)分發(fā)給隊(duì)列了。

等等。。。。。。嗯,好像我們還沒有開啟隊(duì)列,接著。。。。。。。

7)開啟隊(duì)列:

命令行中輸入:php artisan queue:work,work和linsten有啥區(qū)別,自行看手冊嘍。有空行在那閃也暫時(shí)不管它,這代表隊(duì)列已經(jīng)開始工作了,只是我們還沒有把我們的工作發(fā)到隊(duì)列上,

8)現(xiàn)在在瀏覽器中輸入路由地址,過個(gè)10秒到數(shù)據(jù)庫中看下,1號Id的用戶數(shù)據(jù)就被修改了。

以上就是一個(gè)簡單的隊(duì)列使用,下面,我們來探討以下,如何在一個(gè)鏈接中使用多個(gè)隊(duì)列,這是laravel手冊最坑爹的地方,寫的不清不楚,我找了一段別人寫的,抄來大家自己琢磨就好:

這是一個(gè)必然需要考慮到的問題,我不可能將所有任務(wù)都放在一個(gè)叫default的隊(duì)列中,這樣不容易對隊(duì)列進(jìn)行管理。要指定不同的隊(duì)列,非常簡單,在dispatch()后緊接著跟上onQueue()方法即可:

Demo::dispatch()->onQueue('emails');

不對啊,我好像沒有定義過這個(gè)叫 emails 的 queue。嗯,自然需要做出一點(diǎn)改動,在queue.php配置文件中的redis配置queue從default改為{default},這樣做的效果就是隊(duì)列的名稱可以從運(yùn)行的時(shí)候動態(tài)拿到,而不是寫死的。

如果使用 Lumen 框架,那么直接這么寫會報(bào)錯:Call to a member function onQueue() on string。原因在于 Lumen 的 Job 基類中并沒有使用Illuminate\Foundation\Bus\Dispatchable這個(gè)trait,而是直接使用Illuminate\Bus\Queueable中的onQueue()方法。

那么現(xiàn)在就很清楚了,我們的 Job 類使用了Illuminate\Bus\Queueable這個(gè)trait,所以需要在 Job 類上調(diào)用這個(gè)onQueue()方法。

$job =newXXXJob();dispatch($job->onQueue('queue-name'));

當(dāng)我們在開啟隊(duì)列的時(shí)候:

php artisan queue:work --queue=emails

這里指定的隊(duì)列名 emails 和dispatch時(shí)指定的隊(duì)列名保持一致即可。

好了,這兩個(gè)關(guān)卡打通了,手冊后面的東西就沒什么難的了,自個(gè)兒試一試吧?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容