Laravel 使用 Queue

閱讀時間約 12 分鐘
當伺服器需要處理一些比較花時間的任務時(如發送Email、上傳影片等等),讓user等待直到執行完畢,是個很不明智的選擇,這時候就很適合使用Queue,讓工作在背景執行,使用者就能立刻做下一件事,不必在那邊等待。
Queue可以想像成是一個裝待辦事項的容器,把所有需要耗時的任務(Job)往裡面丟,根據排隊順序依序處理。
本篇主要選用database driver來說明如何使用Laravel Queue,其他driver如redis則需要安裝相關套件,不在本篇討論範圍中。

config/queue.php:
這邊放queue connection driver相關設定。

.env: QUEUE_CONNECTION預設是sync
QUEUE_CONNECTION=sync
改成database:
QUEUE_CONNECTION=database
  • sync是local queue,指當job被push到queue後(程式中執行dispatch()),會馬上執行job,而且是用main thread執行job,因此在開發階段才有用。

若要使用database driver,需要建立jobs table來記錄任務,首先透過這個指令,可以快速建立create_jobs_table migration:
$ php artisan queue:table
建立jobs表:
$ php artisan migrate

建立Queue Job指令:
$ php artisan make:job ${name}
假設建立一個SendEmail的job:
$ php artisan make:job SendEmail
  • Queue Jobs在這個路徑: app/Jobs。
  • handle()就是執行這個job的進入點。
  • 當在執行job過程中有exception發生時,這個job會被釋放出來,再重新丟回Queue中retry,直到設定的最大重試次數為止,指定最大重試次數--tries後面會再提到。
  • 如果想手動釋放job,可以使用InteractsWithQueue這個 trait提供的release method:
public function handle()
{
  if (condition) {
    $this->release(10);
  }
}
  • 檢查重試次數: 可以用attempts method:
public function handle()
{
  if ($this->attempts() > 3) {
    //
  }
}

將Job push到Queue中: 在controller中,可以直接這樣寫:
use App\Jobs\SendEmail;
$job = (new SendEmail())->onQueue('email');
$this->dispatch($job);
  • 由於我們.env中的QUEUE_CONNECTION設定為database,所以執行dispatch後,job會被push到database driver中。
  • BTW,如果.env中QUEUE_CONNECTION設定為sync,則會馬上執行SendEmail這個job,並不會塞資料進jobs這張表。
  • ->onQueue('email') 這邊其實是對應到jobs這張表的queue欄位,也就是把job push到email這個queue中。
  • 注意,如果沒有->onQueue('email')這行,預設塞進去的queue name會是default,如下圖。

那麼若要將Job push到Queue中的地方不是在controller呢? 比如是在Service, Repository這些地方:
$job = (new SendEmail())->onQueue('email');
dispatch($job);
  • 事實上,dispatch是一個全域的method,所以可以直接這樣用。
  • 甚至是在Route中,也可以使用dispatch:
Route::get('/user', function () {
  dispatch(new App\Jobs\SendEmail);
  return 'Done!';
});

啟動 Queue Listener: 這邊就是用來執行被push到queue中的jobs:
$ php artisan queue:listen
  • 這邊預設會優先執行default queue的jobs。
  • Queue是先進先出的概念,先塞進來的job優先處理。
  • attempts是指執行次數。
  • created_at是指job被建立的時間。
  • available_at是指job預計執行的時間。
  • reserved_at是指job開始執行的時間。

可以使用--queue來指定優先順序,如下範例,email這個queue中的job永遠會優先被執行,接著才是default這個queue:
$ php artisan queue:listen --queue=email,default

--tries 用來指定job的最大重試次數,如下範例:
php artisan queue:listen --queue=email,default --tries=3
  • 當job發生exception時,這個job會從queue中被釋放(這個job的row會從jobs table中被刪除)然後重新塞一個new job到queue中retry(重新塞一個job row 到jobs table中)。
  • 這個範例表示job執行3次如果還是failed,就不會再重試了。

接著來看個例子,假設SendEmail job code如下:
假設已經塞了三筆email jobs:
接著執行:
$ php artisan queue:listen --queue=email
  • 從console可以發現這三個job被依序處理了。
  • 由於已經啟動queue listener,這時候如果又有job被塞進email queue,若舊的job都被執行完畢了,就會馬上執行這個job。
  • 經過實測,如果沒有加上 --queue=email,會發現都沒反應,應該是因為只會處理default queue jobs,這邊需特別注意。
  • 需特別注意,執行完成的job,該row data會從jobs table中被移除。

如果是希望這個job可以晚一點執行,比如可以延遲5分鐘後再執行:
$job = (new SendEmail())->onQueue('email')->delay(60 * 5);
$this->dispatch($job);
  • 這時候塞進去jobs table中的available_at欄位時間就會晚五分鐘。

處理失敗的job

由於job retry至指定上限次數時,紀錄不會留在jobs table中。因此,可以建立一個failed_jobs table來存放failed job,當retry次數超過設定的上限時,可以將這個job寫入failed_jobs 表中。
  • failed job相關設定在這:
  • 建立fail job migrations & table:
$ php artisan queue:failed-table
$ php artisan migrate
  • trigger這段程式,將job塞入queue中:
use App\Jobs\SendEmail;
$job = (new SendEmail())->onQueue('email');
$this->dispatch($job);
  • 啟動queue listener,用--tries 來指定job的最大重試次數:
$ php artisan queue:listen --queue=email --tries=3
  • 可以看到,SendEmail job執行了三次都failed,所以最終會把fail job紀錄在failed_jobs表,會記錄失敗時間及exception,方便查問題。

  • 查看所有失敗jobs:
$ php artisan queue:failed
  • ID就是table中紀錄的uuid,可以用來重試job:
$ php artisan queue:retry ${ID}
  • 重試所有failed job:
$ php artisan queue:retry all
  • 刪除某個失敗的job: 這筆job會從failed_jobs表中刪除。
$ php artisan queue:forget ${ID}
  • 刪除所有失敗的job: 所有job會從failed_jobs表中刪除。
$ php artisan queue:flush

另外,可以在job class中定義一個failed() method,用來處理當job失敗時要做什麼事情。
  • 如同上面的例子,--tries=3 表示若job fail會retry3次,如果第3次還是失敗,就視為真正失敗,會trigger這個failed() method。
  • 如果沒有加--tries參數,則執行一次失敗就會trigger failed() method了。

在生產環境啟動Queue Listener

  • 在production環境不要用queue:listen來啟動,queue:listen適合在local開發時候用,更動程式碼會馬上生效,不須重新啟動。
  • 相反的,在production環境應該要用queue:work,queue:work有daemon選項可以在背景啟動,CPU用量也比較低。
$ php artisan queue:work --daemon --queue=email --tries=3
  • queue:work指令用法跟listen幾乎是一樣的,可以用這個指令查看用法:
$ php artisan help queue:work
  • 由於在背景執行的queue listener是一個長時間的process,啟動的應用程式狀態會被儲存在memory中,所以只要程式有更改,就必須重新啟動:
$ php artisan queue:restart
這個指令會告訴所有queue listener,在執行完目前job後重新啟動,所以不會有job遺失的問題。

監控Queue Listener

確保Queue中的所有job都正常運作,是最重要的事情,那麼當queue listener啟動失敗或發生狀況的時候該怎麼辦呢? job不就都卡住了嗎?
其實可以透過Supervisor來幫忙做監控,Supervisor 是在 Linux 作業系統上的process監控軟體,用它來啟動和監控queue:work process,可以在queue:work指令失敗時自動重啟。
由於本文已經過於冗長了,這部分就等之後有機會再另外寫一篇來分享囉!

為什麼會看到廣告
avatar-img
21會員
161內容數
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Vic Lin的沙龍 的其他內容
建立middleware指令: 假設建立一個Test middleware: 新增的middleware檔案會在app/Http/Middleware路徑中。 註冊Middleware: Global Middleware: 2. Route Middleware route group用法如下:
Laravel 提供了快速套用軟刪除的方法,直接在Model中加上use SoftDeletes即可: 接著在程式中,假設要把文章1刪除,可以直接這樣寫: 另外,如果我再執行一次上述的Article::find(1)->delete(); 會發現有error: 所以上述改成這樣:
如上篇,使用Migration來做DB版本控制,但是會發現開發過程中若是要建立測試資料,要進DB一筆一筆手動新增或執行預先寫好的insert sql,其實有點麻煩,使用Laravel提供的Seeder功能,就可以解決這個問題。 指令如下,假設建立一個user table seeder:
在Laravel中除了用Validator來驗證資料,還可以用Form Request Validation,建立一個驗證class,在request進入controller之前,會先在這邊做驗證,若驗證失敗則不會繼續執行Controller。 建立form request: 範例程式碼:
資料情境: 每個產品有多種顏色,每種顏色屬於一個產品(一對多)。 程式碼: Product Model: 本筆記參考: 1. https://stackoverflow.com/questions/40468976/inserting-with-relationships-in-laravel
安裝設置: 安裝完成後,專案目錄結構如下: 執行$ npm run dev 可直接透過http://localhost:3000進入首頁,對應到pages/index.js。 Prod記得每次要先build再start: Example: SSG: 透過外部api取得資料建立靜態頁面 結論:
建立middleware指令: 假設建立一個Test middleware: 新增的middleware檔案會在app/Http/Middleware路徑中。 註冊Middleware: Global Middleware: 2. Route Middleware route group用法如下:
Laravel 提供了快速套用軟刪除的方法,直接在Model中加上use SoftDeletes即可: 接著在程式中,假設要把文章1刪除,可以直接這樣寫: 另外,如果我再執行一次上述的Article::find(1)->delete(); 會發現有error: 所以上述改成這樣:
如上篇,使用Migration來做DB版本控制,但是會發現開發過程中若是要建立測試資料,要進DB一筆一筆手動新增或執行預先寫好的insert sql,其實有點麻煩,使用Laravel提供的Seeder功能,就可以解決這個問題。 指令如下,假設建立一個user table seeder:
在Laravel中除了用Validator來驗證資料,還可以用Form Request Validation,建立一個驗證class,在request進入controller之前,會先在這邊做驗證,若驗證失敗則不會繼續執行Controller。 建立form request: 範例程式碼:
資料情境: 每個產品有多種顏色,每種顏色屬於一個產品(一對多)。 程式碼: Product Model: 本筆記參考: 1. https://stackoverflow.com/questions/40468976/inserting-with-relationships-in-laravel
安裝設置: 安裝完成後,專案目錄結構如下: 執行$ npm run dev 可直接透過http://localhost:3000進入首頁,對應到pages/index.js。 Prod記得每次要先build再start: Example: SSG: 透過外部api取得資料建立靜態頁面 結論:
你可能也想看
Google News 追蹤
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
我們在「【Message Queue - Kafka】串流時代的超入門簡介」有介紹到關於Kafka的基礎概念, 那麼本章節主要著重於生產者(Producer)的面向來細部探討, 看看生產者(Producer)究竟是什麼? 有哪些應該要注意的? 我們今天的主題除了說明生產者(Producer)的
Thumbnail
我們在「【Message Queue - Kafka】不斷的試誤…, 用Docker來嘗試安裝Kafka」有介紹如何架設kafka, 其中我們使用環境變數來進行kafka的配置, 但除了環境變數之外, 其實還能夠用檔案配置的方式來對kafka進行配置, 如此一來我們就可以將配置檔與啟動檔完全分開,
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
KSQL引擎, 串流形式的SQL? 聽了應該霧煞煞吧! 想像一下傳統的SQL, 是不是一個指令一個動作, 每發送一個指令之後就必須等到查詢/寫入…動作皆完成之後才回應, 然而在Streaming的應用上這顯然不太可行, 每分每秒都有資料流入的情境下, 資料的狀態都在變化, 假設我們一個指令一個動作,
Thumbnail
訊息的即時傳遞已然成為現代社會的趨勢了, 而扮演中樞平台的系統架構功能也漸趨複雜完整, Kafka是一個事件流平台, 正好滿足串流時代之下的即時訊息傳遞架構, 因此我們有必要深入來學習這套事件流平台, 不論是自動化、金融交易、IOT、物流…皆離不開即時的需求, 所以就讓我們蹲好馬步來好好的學習一
Thumbnail
(1)事前LINE主動發出通知,無論何處,所有人均可掌握值勤排班內容  (2) 換班前幾天收到通知,可事先安排交代事務給下回值勤人員,讓值勤業務能順利實施
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
在使用laravel中的Queue job的時候 如果希望job中斷還可以重新啟動這個時候就會需要用到Supervisor了 本篇文章為您帶來如何使用Supervisor執行Laravel的queue:work的教學
Thumbnail
第一份正職工作 在iot公司擔任後端工程師,一上工就使用先前沒用過的php/laravel,也馬上負責公司產品的架構規劃,先前資料庫只有簡單記載使用者跟使用者的一些設定,很多地方有資料不一致的問題,產品內容還有很多實體的關係沒有被定義進資料庫都是這次改版我要做的事情。 改版納入公司、機器
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
我們在「【Message Queue - Kafka】串流時代的超入門簡介」有介紹到關於Kafka的基礎概念, 那麼本章節主要著重於生產者(Producer)的面向來細部探討, 看看生產者(Producer)究竟是什麼? 有哪些應該要注意的? 我們今天的主題除了說明生產者(Producer)的
Thumbnail
我們在「【Message Queue - Kafka】不斷的試誤…, 用Docker來嘗試安裝Kafka」有介紹如何架設kafka, 其中我們使用環境變數來進行kafka的配置, 但除了環境變數之外, 其實還能夠用檔案配置的方式來對kafka進行配置, 如此一來我們就可以將配置檔與啟動檔完全分開,
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
KSQL引擎, 串流形式的SQL? 聽了應該霧煞煞吧! 想像一下傳統的SQL, 是不是一個指令一個動作, 每發送一個指令之後就必須等到查詢/寫入…動作皆完成之後才回應, 然而在Streaming的應用上這顯然不太可行, 每分每秒都有資料流入的情境下, 資料的狀態都在變化, 假設我們一個指令一個動作,
Thumbnail
訊息的即時傳遞已然成為現代社會的趨勢了, 而扮演中樞平台的系統架構功能也漸趨複雜完整, Kafka是一個事件流平台, 正好滿足串流時代之下的即時訊息傳遞架構, 因此我們有必要深入來學習這套事件流平台, 不論是自動化、金融交易、IOT、物流…皆離不開即時的需求, 所以就讓我們蹲好馬步來好好的學習一
Thumbnail
(1)事前LINE主動發出通知,無論何處,所有人均可掌握值勤排班內容  (2) 換班前幾天收到通知,可事先安排交代事務給下回值勤人員,讓值勤業務能順利實施
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
在使用laravel中的Queue job的時候 如果希望job中斷還可以重新啟動這個時候就會需要用到Supervisor了 本篇文章為您帶來如何使用Supervisor執行Laravel的queue:work的教學
Thumbnail
第一份正職工作 在iot公司擔任後端工程師,一上工就使用先前沒用過的php/laravel,也馬上負責公司產品的架構規劃,先前資料庫只有簡單記載使用者跟使用者的一些設定,很多地方有資料不一致的問題,產品內容還有很多實體的關係沒有被定義進資料庫都是這次改版我要做的事情。 改版納入公司、機器
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更