當前位置:名人名言大全網 - 端午節短信 - laravel如何檢測隊列是否正在執行?

laravel如何檢測隊列是否正在執行?

隊列服務

Laravel將所有與隊列相關的服務封裝在壹個服務中,並通過隊列服務提供者在IOC中註冊。隊列服務中提供了許多服務。它做了什麽請看下面代碼裏的註釋(有幾個代碼,我就不全貼了)。

公共函數寄存器()

{

//註冊管理器,這是隊列服務的統壹入口。

$ this-& gt;register manager();

//註冊隊列的各種命令

$ this-& gt;register worker();

$ this-& gt;register listener();

//註冊任務執行失敗後的記錄

$ this-& gt;registerFailedJobServices();

//未知

$ this-& gt;registerQueueClosure();

}

談論配置文件

隊列服務註入IOC後,我們就可以使用隊列了。

隊列服務最基本的就是把任務寫入隊列,然後取出來執行,非常簡單。因此,隊列服務有四個基本要素:任務、進入隊列、離開隊列和執行。在這裏,我們將按照這四個元素的步驟,看看它們是如何在laravel中實現的。這裏先用官方的例子來分析,等有了具體的概念後再舉壹反三,學習其精髓。

工作

隊列服務圍繞著任務。在手冊中,我們可以通過它的例子SendReminderEmail清楚地知道laravel可以為壹個任務做很多事情,比如設置重新執行的次數,表示任務(如果失敗)可以執行多次(針對單個作業);可以設置執行是否可以延遲;為作業設置隊列名稱,等等。這些功能由\照明\總線\隊列提供。當然,實例中還有壹個\ illuminate \ queue \ interactswithqueue,用於作業(後面)。任務建立後,需要將其放入隊列中。當然,除了以上特點,還有任務的執行邏輯等。要完全理解任務,就要理解它的數據結構,它在隊列中的數據結構會在入口隊列中提到。

隊列中的任務

在該示例中,定義任務後,通過控制器中的方法將任務放入隊列中。這是如何實現的?

代碼是:\ Illuminate \ Foundation \ Bus \ dispatch Jobs。

受保護的功能分派($job)

{

return app(Dispatcher::class)-& gt;派遣(工作);

}

/**

*將命令分派給當前進程中相應的處理程序。

*

* @param混合$job

* @返回混合

*/

公共函數dispatchNow($job)

{

return app(Dispatcher::class)-& gt;dispatch now($ job);

}

這段代碼的用意是理解app(Dispatcher::class),在\ Illuminate \ Bus \ busserviceprovider(為什麽不在這裏分析)中表達的很清楚,app(Dispatcher::class)是\Illuminate\Bus\Dispatcher。現在,上面代碼中的dispatch和dispatchNow方法將逐漸清晰。簡而言之,Dispatcher類做了兩件事,執行任務或者將任務放入隊列,也就是將隊列任務分為兩種執行模式,即立即執行或者消息隊列執行。與隊列相關的代碼如下:

/**

*將任務分配給隊列。

* @ param string $命令任務類

*/

公共函數dispatchToQueue($command)

{

$ connection = isset($ command-& gt;連接)?$ command-& gt;連接:空;

// laravel內置了各種隊列服務,這裏解析壹下。

$ queue = call _ user _ func($ this-& gt;queueResolver,$ connection);

//如果隊列服務解析不成功,則引發異常。

如果(!$queue instanceof Queue) {

引發new RuntimeException('隊列解析程序未返回隊列實現。);

}

//可以自定義隊列方法,在任務類中進入隊列。

if (method_exists($command,' queue '){

return $ command-& gt;queue($queue,$ command);

}否則{

//系統提供的隊列入口方法。

return $ this-& gt;pushCommandToQueue($queue,$ command);

}

}

/**

*根據不同的任務屬性選擇不同的方式進入隊列。

*這裏提到的方法,手冊裏都有提到。

* @ param Queue$Queue隊列服務

* @ param $命令任務類

*/

受保護的函數pushCommandToQueue($queue,$command)

{

//推送任務設置延遲和隊列名稱。

if(isset($ command-& gt;queue,$ command-& gt;延遲)){

return $ queue-& gt;laterOn($ command-& gt;queue,$ command-& gt;延時,$ command);

}

//設置隊列名稱

if(isset($ command-& gt;隊列)){

return $ queue-& gt;pushOn($ command-& gt;queue,$ command);

}

//設置延遲

if(isset($ command-& gt;延遲)){

return $ queue-& gt;稍後($ command-& gt;延時,$ command);

}

//默認

return $ queue-& gt;push($ command);

}

到目前為止,控制器已經演示了壹種進入隊列的方法。顯然,它是壹個封裝的接口。雖然很有用,但是有些操作我們並不需要,比如是否立即執行,進入隊列時設置不同的任務參數等。如果我們需要更好地使用它,我們應該更深入,找出它進入隊列的關鍵點(它與Redis交互的地方)。上面已經提到了Call _ user _ func。queueResolver,$ connection);會得到壹個隊列服務,那麽$ this->;什麽是queueResolver?可以在\ Illuminate \ Bus \ Bus service provider:23:

//要理解這個回調,需要理解Illuminate \ contracts \ queue \ factory。

//可以在vendor/laravel/framework/src/Illuminate/foundation/application . PHP中看到它與Illuminate\Queue\QueueManager的關系:1051。

$ this-& gt;app-& gt;singleton('照明\總線\調度',功能($app) {

返回新的調度程序($app,function($ connection = null)use($ app){

return $ app[' Illuminate \ Contracts \ Queue \ Factory ']-& gt;連接($ connection);

});

});

隊列管理器

在laravel中,服務的統壹接口是它的管理器,與所需服務的交互基本由__call方法提供,這有兩個好處:壹是提供了統壹的接口,二是層次清晰(實際處理由__call轉發,配置相關問題由管理器自己解決)。

現在為了讓任務進入隊列的過程更加清晰,壹步壹步找到了QueueManager。該類設置了許多事件接口和其他與連接相關的方法,其中connection方法顯示了如何解析隊列服務。

事實上,這段解析代碼的唯壹難點在於:

受保護的函數getConnector($driver)

{

if(isset($ this-& gt;連接器[$driver])) {

return call _ user _ func($ this-& gt;連接器[$ driver]);

}

拋出新的InvalidArgumentException("沒有[$driver]的連接器");

}

為什麽這麽簡單的代碼就能解析隊列服務?壹眼就能看出QueueServiceProvider。其中註冊了許多隊列服務。redis的隊列服務處理是\Illuminate\Queue\RedisQueue。

隊列管理器的功能現在非常清楚了。1,解析隊列服務2,轉發(__call)到對應的隊列服務3,提供隊列相關接口。既然QueueManager有這麽多隊列相關的函數,我們完全可以把它作為隊列處理的壹個入口(直接獲取隊列服務再進行操作並不是明智的選擇)。無獨有偶,laravel也這麽做了。所以現在有兩種進入隊列的方式,1,使用\ Illuminate \ Foundation \ Bus \ dispatch Jobs與隊列服務間接通信,使用QueueManager與隊列服務間接通信。當然,這些方法都是在\Illuminate\Queue\RedisQueue(隊列服務的接口)上擴展的。所以,

再排隊

lavavel中的隊列服務有很多種,這裏只分析和研究Redis隊列服務。因此,對隊列的處理集中在照明隊列重排隊上。如上所述,有兩種進入隊列的方法,分別使用,看看它們生成的任務數據結構有什麽區別。(數據結構很好分析,後面會提到。)

在控制器中使用$ this->命令;dispatch((new SendReminderEmail()));也就是說,作為任務類輸入。

{

" job ":" Illuminate \ \ Queue \ \ CallQueuedHandler @ call ",

"數據":{

" command ":" O:26:\ " App \ \ Jobs \ \ SendReminderEmail \ ":4:{ s:5:\ " queue \ ";學生:5:“電子郵件”;s:10:\ "連接\ ";n;s:5:\ "延遲\ ";n;s:6:\ " \ u 0000 * \ u 0000 job \ ";n;}"

},

" id ":" 7u 00 jimd 8 cans 0 fqo 8 Jed qkqmnbqsfsr ",

【嘗試次數】:1

}

直接使用queue::push(sendreminderemail::class,[' email ' = > ' 123456789 @ QQ . com '],' email ');

{

" job ":" App \ \ Jobs \ \ SendReminderEmail ",

"數據":{

“郵箱”:“123456789@qq.com”

},

" id ":" i0oebiqjjisqrz 7 stx 3 zexrblf 7 uilx ",

【嘗試次數】:1

}

如上所述,形成壹個消息隊列需要兩個進程,所以上面進入隊列的是壹個進程,現在退出隊列和執行任務在另壹個進程中執行。Lavarel提供了兩個命令來啟動這個進程,queque: work,queue: litsen。當然,在了解了如何完成這些操作之後,妳可以自己編寫壹個命令。現在讓我們看看它是如何出列和執行任務的。

任務不在隊列中

在手冊中,您可以為任務指定各種屬性,如延遲、失敗次數、隊列名稱等。當然,所有可執行的操作或功能都依賴於數據結構,數據結構被公式化以實現相應的行為。所以比較容易理解上面數據結構對應的RedisQueue的代碼。

RedisQueue是所有隊列服務(Redis)的基礎接口,所以任務離開隊列的操作也可以在這裏找到。假設妳已經熟悉了RedisQueue的代碼,不難發現有壹個稍微復雜壹點的pop方法(dequeue)。那麽,問題就來了。出列是如何實現的?通過解決這個問題,當任務不在隊列中時,就可以認為它已經完成了。

隊列的固有功能

查看php artisan queue:work - help命令的用法,整理出隊列需要的函數或服務:

指定隊列名稱

任務執行邏輯

任務執行延遲

任務中的最大失敗次數

當然,關於這個命令還有其他功能,比如是否作為守護進程執行,是否強制執行,限制進程執行的內存,沒有任務時的等待時間。這些與命令相關的功能因命令而異,與隊列任務無關。這樣,在明確了隊列任務需要的函數之後,就可以分析它的數據結構,理解代碼了。

隊列數據結構

數據結構是根據行為建立的。所以在看pop方法的時候,可以考慮以上幾點。在上面的數據結構中,我們已經可以看到隊列的執行邏輯,需要的參數,失敗次數,壹目了然,就不贅述了。在整個pop方法中,有這麽多隊列,queue: delayed,queue: reserved,queue。本來壹個任務可以用lpop完成。為什麽?因為任務延遲和失敗處理。

其實現過程如下圖所示:

流程分析:

(1).取壹個任務,有序集合中的分數就是到期時間,到期時間意味著在這個時間之前不執行,從而達到延遲執行的效果。這裏延遲的意思是很多秒後不執行,而是很多秒不執行。對於壹個過期的任務,rpush它的r到隊列中,直到lpop操作取走它。

(1).為什麽隊列:保留集存在,lpop的任務zadd分支?因為只要lpop創建了壹個作業,它就可以被記錄。如果進程在任務開始執行前異常終止,任務不會丟失。再次執行時,可按上述步驟取出,防止作業意外丟失。

(2)隊列的執行基於json中的類,相對簡單,省略。

(3)當任務執行成功後,手動刪除隊列中的任務:保留;當任務執行失敗時,刪除queue:reserved中的任務,然後記錄。記錄方式為zadd queue:delayed,任務執行次數加壹。此流程已被封裝(RedisQueue:: release)。

這壹系列過程完成了延遲隊列任務的功能。所以所有這些復雜的操作都是為了實現延時的功能。當然,還有更好的想法可以考慮實現自己。

執行任務

至此,任務的執行在json數據結構中清晰的展現出來,整個處理過程也壹目了然。需要註意的是,任務執行成功後應該刪除任務。對於如何執行隊列,如何執行排隊的任務,可以詳細看看queue:work command(\ illuminate \ queue \ console \ work command::fire),這是最好的例子;

隊列處理命令的定制

使用queue:work之後,妳會發現它並不能處理所有的情況。所以本文中已經提到,自己編寫壹個處理命令是可行的。面對排隊:工作解決不了的問題,可以考慮自己寫。在實際開發中,任務的種類很多,不同的任務應該有不同的處理方案。因此,經常會遇到以下問題:

例如:

調用服務出現錯誤,是服務提供者造成的,需要單獨記錄,這樣的錯誤不算作業的執行錯誤。

營銷消息只能在9:00到20:00之間發送,所以在這個時間段沒有必要執行。

與數據庫交互時,數據庫連接有時間限制,但作為守護進程執行沒有時間限制,所以會報錯。

因此,面對laravel提供的命令的局限性,需要具備定制命令的能力。