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指令失敗時自動重啟。
由於本文已經過於冗長了,這部分就等之後有機會再另外寫一篇來分享囉!

為什麼會看到廣告
21會員
161內容數
留言0
查看全部
發表第一個留言支持創作者!
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
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
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
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
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