1.隊列的應(yīng)用場景:
PHP在異步編程上的短板是眾所周知的瞻润,這也是當年P(guān)HP能夠迅速火起來的一個重要特性喘垂,當然甜刻,這也是Nodejs能夠火起來的一個相反方向的重要特性(Nodejs的亮點就是高并發(fā)的處理性能,只是安全性嘛正勒,仁者見智了)得院,PHP為保持語言的簡單,設(shè)計之初不考慮多線程章贞,直至PHP7祥绞,有了TSRM這樣的多線程并發(fā)處理機制,PHP 默認依然不支持多線程鸭限,如果一定要使用多線程蜕径,還需要安裝 pthread 擴展。盡管并發(fā)處理這塊一致是PHP的短板败京,同時兜喻,大并發(fā)處理的相關(guān)技術(shù)在PHP領(lǐng)域好像也是小妾生的兒子般不受待見,但是并不代表需求中就不需要這些東西赡麦,于是乎就有了并發(fā)的另外一種替代方案朴皆,就是我們今天所要瞎掰的,隊列隧甚。
可見车荔,隊列就是為了在某種程度上替代多線程而設(shè)計的一種處理并發(fā)的方式,同時戚扳,也就具備天生的秉性:異步忧便!用于處理耗時的工作,比如一個流程走到一個地方帽借,要發(fā)送一封郵件通知珠增,那我不能讓整個程序等到郵件發(fā)完了再繼續(xù)下去,那我就把發(fā)郵件的工作丟到一個隊列中去砍艾,讓這個工作慢慢做蒂教,我繼續(xù)處理響應(yīng)的邏輯。整個過程其實也就這么回事脆荷,沒有很困難凝垛。
2.laravel中的隊列的實現(xiàn)
閑話不扯,現(xiàn)在有了laravel蜓谋,我們不用自己設(shè)計隊列的實現(xiàn)機制梦皮,我們來看看怎么用就好了。
1)首先桃焕,我們要先弄清楚幾個基本概念剑肯,我們先來想想,我們要把一項一項的工作任務(wù)放到隊列里观堂,最樸素的想法让网,我們會怎么做呀忧?我們是不是先得給任務(wù)起個名字,然后至少得給它一段執(zhí)行的代碼吧溃睹,然后再把這一個任務(wù)作為一個整體的數(shù)據(jù)結(jié)構(gòu)放到一個叫做隊列的數(shù)據(jù)結(jié)構(gòu)中去而账,那要把名字和執(zhí)行的代碼關(guān)聯(lián)起來,肯定是用鍵值對的方式最方便了丸凭,所以福扬,怎么做呢?最簡單就是用數(shù)組嘍惜犀,一個數(shù)組铛碑,然后中間一堆鍵值對,鍵名是任務(wù)名虽界,鍵值是任務(wù)的執(zhí)行代碼汽烦。但是數(shù)組有一個問題,數(shù)組的檢索是一個完了接一個莉御,我沒法控制它中間執(zhí)行的異步操作捌餐獭?但是它能夠模仿出這些操作模式礁叔,所以牍颈,在laravel中,就有了一個驅(qū)動名琅关,叫sync煮岁,同步嘛,用來做測試用的涣易。
2)但是画机,如果執(zhí)行代碼太大,然后任務(wù)太多新症,怎么辦步氏?都擺到數(shù)組里總是不合適吧?那我們是不是就得考慮把這些鍵值對給它放到數(shù)據(jù)庫里面去徒爹?嗯荚醒,不錯,這是一種好思路隆嗅,也有人這么做腌且,同樣的,laravel也提供數(shù)據(jù)庫做隊列的驅(qū)動榛瓮,和前面一樣,反正我的目的就是把任務(wù)讓他們排隊去巫击,只是一個一個塞到數(shù)據(jù)庫禀晓,然后一個一個取出來執(zhí)行精续。
3)然后,就會有人想到了粹懒,我干嘛不采用更專業(yè)的軟件來做這個事重付?比如redis?人家天生就是鍵值對處理的凫乖,而且和數(shù)據(jù)庫不通确垫,redis的數(shù)據(jù)是在內(nèi)存中的,這樣明顯速度就提上來了帽芽,我們會這么想删掀,laravel團隊也會這么想,于是有了redis驅(qū)動导街。
4)然后披泪,有人想到了這么多方法,自然就有人想專業(yè)提供這些服務(wù)的搬瑰,于是就有比如亞馬遜這樣的公司提供一系列的產(chǎn)品來支持款票,就有了SQS這些驅(qū)動,等等等等泽论。艾少。。翼悴。
其實說白了缚够,就是把任務(wù)的鍵名和鍵值儲存起來而已,儲存的媒介用什么都可以抄瓦,而所謂的隊列驅(qū)動潮瓶,就是把儲存在這些媒介中的任務(wù)代碼拿住來按照隊列的算法進行執(zhí)行的一種調(diào)配方式的一段代碼而已。
3.講完了這些钙姊,我們來看看怎么實現(xiàn)
我們以redis為例毯辅,來看看laravel怎么做隊列的:
1)一上來沒說的,安裝redis驅(qū)動:composer require 'predis/predis ~1.0'
當然煞额,您得先有redis思恐,如果系統(tǒng)沒裝,apt-get一個膊毁,yum一個隨您啦胀莹。
2)接下來,要把任務(wù)放隊列婚温,我們得先做一個任務(wù)出來,我們寫個簡單的提交用戶數(shù)據(jù)的任務(wù)吧:
執(zhí)行: php artisan make:job StoreUser
3)執(zhí)行完上述命令描焰,在App下多一個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ā)送失敗');
? ? }
}
上面這個任務(wù)很簡單荆秦,就是依賴注入一個User模型篱竭,然后在handle方法里面寫一個用戶數(shù)據(jù)處理的方法,最后儲存步绸,沒設(shè)么意義掺逼,我們只是演示而已。
其實瓤介,也可以不用注入模型吕喘,比如可以寫一個服務(wù),然后把服務(wù)注入進來刑桑,反正是個類就行氯质。
4)接下來,我們告訴框架漾月,要用哪個驅(qū)動病梢,到.env下面寫這兩句話:
QUEUE_CONNECTION=redis
REDIS_QUEUE=email
這兩個配置,是配置所謂的“鏈接名”和“隊列名”梁肿。
所謂鏈接蜓陌,就是我們要存儲的哪一個存儲介質(zhì),好比銀行吩蔑,
隊列名钮热,就是我們要使用哪一條隊列。
5)這樣烛芬,如果使用的是單隊列隧期,其他就啥都不用管了,這下隊列已經(jīng)可以用了赘娄。文章最后我們再來探討一個鏈接多個隊列的情況仆潮。
6)然后我們寫一個路由,一個控制器:
路由:Route::get('/mail','Mail\UserQueueController@queue');
控制器的方法代碼:
public function queue()
{
? ? $user=User::find(1);
? ? Test::dispatch($user)->delay(10);
}
這樣遣臼,我們就可以把任務(wù)分發(fā)給隊列了性置。
等等。揍堰。鹏浅。。屏歹。隐砸。嗯,好像我們還沒有開啟隊列蝙眶,接著季希。。。胖眷。武通。。珊搀。
7)開啟隊列:
命令行中輸入:php artisan queue:work,work和linsten有啥區(qū)別尾菇,自行看手冊嘍境析。有空行在那閃也暫時不管它,這代表隊列已經(jīng)開始工作了派诬,只是我們還沒有把我們的工作發(fā)到隊列上劳淆,
8)現(xiàn)在在瀏覽器中輸入路由地址,過個10秒到數(shù)據(jù)庫中看下默赂,1號Id的用戶數(shù)據(jù)就被修改了沛鸵。
以上就是一個簡單的隊列使用,下面缆八,我們來探討以下曲掰,如何在一個鏈接中使用多個隊列,這是laravel手冊最坑爹的地方奈辰,寫的不清不楚栏妖,我找了一段別人寫的,抄來大家自己琢磨就好:
這是一個必然需要考慮到的問題奖恰,我不可能將所有任務(wù)都放在一個叫default的隊列中吊趾,這樣不容易對隊列進行管理。要指定不同的隊列瑟啃,非常簡單论泛,在dispatch()后緊接著跟上onQueue()方法即可:
Demo::dispatch()->onQueue('emails');
不對啊,我好像沒有定義過這個叫 emails 的 queue蛹屿。嗯屁奏,自然需要做出一點改動,在queue.php配置文件中的redis配置queue從default改為{default}蜡峰,這樣做的效果就是隊列的名稱可以從運行的時候動態(tài)拿到了袁,而不是寫死的。
如果使用 Lumen 框架湿颅,那么直接這么寫會報錯:Call to a member function onQueue() on string载绿。原因在于 Lumen 的 Job 基類中并沒有使用Illuminate\Foundation\Bus\Dispatchable這個trait,而是直接使用Illuminate\Bus\Queueable中的onQueue()方法油航。
那么現(xiàn)在就很清楚了崭庸,我們的 Job 類使用了Illuminate\Bus\Queueable這個trait,所以需要在 Job 類上調(diào)用這個onQueue()方法。
$job =newXXXJob();dispatch($job->onQueue('queue-name'));
當我們在開啟隊列的時候:
php artisan queue:work --queue=emails
這里指定的隊列名 emails 和dispatch時指定的隊列名保持一致即可怕享。
好了执赡,這兩個關(guān)卡打通了,手冊后面的東西就沒什么難的了函筋,自個兒試一試吧沙合?