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
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在使用laravel中的Queue job的時候 如果希望job中斷還可以重新啟動這個時候就會需要用到Supervisor了 本篇文章為您帶來如何使用Supervisor執行Laravel的queue:work的教學
Thumbnail
為了做登入log紀錄,練習mongoDB來存放log資料,這邊就來紀錄 Laravel 配置mongoDB的實作,而原本我的side project是練習搭建docker開發環境,故這邊會以dockerfile來設定跟紀錄踩坑問題。
Thumbnail
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
Thumbnail
本篇深入探討了Model的基本定義、其Controller的關係,以及如何在Controller中使用Model進行CRUD操作。強調了Model的關聯方法,如hasMany,並透過範例程式碼展示了其實際應用。為初學者提供了一個清晰的Laravel入門路徑,同時也為有經驗的開發者提供了實用的參考。
Thumbnail
Laravel的Model是資料和邏輯的核心連接,簡化資料庫操作。本指南著重於Model的基本屬性、方法和Eloquent ORM的使用。我們詳細探討了hasMany、hasOne和belongsTo這些關聯,它們基於外鍵確定資料間的關係。透過本文,您將深入了解如何有效地在Laravel中使用Mod
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在使用laravel中的Queue job的時候 如果希望job中斷還可以重新啟動這個時候就會需要用到Supervisor了 本篇文章為您帶來如何使用Supervisor執行Laravel的queue:work的教學
Thumbnail
為了做登入log紀錄,練習mongoDB來存放log資料,這邊就來紀錄 Laravel 配置mongoDB的實作,而原本我的side project是練習搭建docker開發環境,故這邊會以dockerfile來設定跟紀錄踩坑問題。
Thumbnail
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
Thumbnail
本篇深入探討了Model的基本定義、其Controller的關係,以及如何在Controller中使用Model進行CRUD操作。強調了Model的關聯方法,如hasMany,並透過範例程式碼展示了其實際應用。為初學者提供了一個清晰的Laravel入門路徑,同時也為有經驗的開發者提供了實用的參考。
Thumbnail
Laravel的Model是資料和邏輯的核心連接,簡化資料庫操作。本指南著重於Model的基本屬性、方法和Eloquent ORM的使用。我們詳細探討了hasMany、hasOne和belongsTo這些關聯,它們基於外鍵確定資料間的關係。透過本文,您將深入了解如何有效地在Laravel中使用Mod