Laravel 使用 Queue

更新 發佈閱讀 12 分鐘

當伺服器需要處理一些比較花時間的任務時(如發送Email、上傳影片等等),讓user等待直到執行完畢,是個很不明智的選擇,這時候就很適合使用Queue,讓工作在背景執行,使用者就能立刻做下一件事,不必在那邊等待。

Queue可以想像成是一個裝待辦事項的容器,把所有需要耗時的任務(Job)往裡面丟,根據排隊順序依序處理。

本篇主要選用database driver來說明如何使用Laravel Queue,其他driver如redis則需要安裝相關套件,不在本篇討論範圍中。


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

vocus|新世代的創作平台
vocus|新世代的創作平台
vocus|新世代的創作平台

.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
vocus|新世代的創作平台

建立jobs表:

$ php artisan migrate
vocus|新世代的創作平台

建立Queue Job指令:

$ php artisan make:job ${name}

假設建立一個SendEmail的job:

$ php artisan make:job SendEmail
vocus|新世代的創作平台
  • 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這張表。
vocus|新世代的創作平台
  • ->onQueue('email') 這邊其實是對應到jobs這張表的queue欄位,也就是把job push到email這個queue中。
  • 注意,如果沒有->onQueue('email')這行,預設塞進去的queue name會是default,如下圖。
vocus|新世代的創作平台

那麼若要將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優先處理。
vocus|新世代的創作平台
  • 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如下:

vocus|新世代的創作平台

假設已經塞了三筆email jobs:

vocus|新世代的創作平台

接著執行:

$ php artisan queue:listen --queue=email
vocus|新世代的創作平台
  • 從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欄位時間就會晚五分鐘。
vocus|新世代的創作平台

處理失敗的job

由於job retry至指定上限次數時,紀錄不會留在jobs table中。因此,可以建立一個failed_jobs table來存放failed job,當retry次數超過設定的上限時,可以將這個job寫入failed_jobs 表中。

  • failed job相關設定在這:
vocus|新世代的創作平台
  • 建立fail job migrations & table:
$ php artisan queue:failed-table
$ php artisan migrate
vocus|新世代的創作平台
  • 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
vocus|新世代的創作平台
vocus|新世代的創作平台
  • 可以看到,SendEmail job執行了三次都failed,所以最終會把fail job紀錄在failed_jobs表,會記錄失敗時間及exception,方便查問題。

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

另外,可以在job class中定義一個failed() method,用來處理當job失敗時要做什麼事情。

vocus|新世代的創作平台
vocus|新世代的創作平台
  • 如同上面的例子,--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指令失敗時自動重啟。

由於本文已經過於冗長了,這部分就等之後有機會再另外寫一篇來分享囉!




本筆記參考:
1. https://laravel.tw/docs/5.2/queues
2. https://ithelp.ithome.com.tw/articles/10261700?sc=rss.iron
3. https://tn710617.github.io/zh-tw/LaravelQueueWithSQS/
4. https://laravel-news.com/queuelisten

留言
avatar-img
Vic Lin的沙龍
20會員
161內容數
Vic Lin的沙龍的其他內容
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
看更多
你可能也想看
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
比如訂單出貨的時候,觸發一個訂單出貨事件,發送出貨email通知給user。 需先註冊event與listener,在EventServiceProvider的$listen中定義: 產生event與listener: 下指令可以方便產生事件與監聽器檔案: 產生的事件與監聽器如下: 事件訂閱者
Thumbnail
比如訂單出貨的時候,觸發一個訂單出貨事件,發送出貨email通知給user。 需先註冊event與listener,在EventServiceProvider的$listen中定義: 產生event與listener: 下指令可以方便產生事件與監聽器檔案: 產生的事件與監聽器如下: 事件訂閱者
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
Laravel Migrate可以用來做資料庫版本控制,對開發團隊來說,可以快速修改Schema,了解每個工程師做了什麼change,比如新增、修改哪些欄位、表格等等,是非常實用的功能。 在開始之前,須先建立好DB,並設定好.env中的DB連線config。 建立migrate指令:
Thumbnail
Laravel Migrate可以用來做資料庫版本控制,對開發團隊來說,可以快速修改Schema,了解每個工程師做了什麼change,比如新增、修改哪些欄位、表格等等,是非常實用的功能。 在開始之前,須先建立好DB,並設定好.env中的DB連線config。 建立migrate指令:
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當伺服器需要處理一些比較花時間的任務時(如發送Email、上傳影片等等),讓user等待直到執行完畢,是個很不明智的選擇,這時候就很適合使用Queue,讓工作在背景執行,使用者就能立刻做下一件事,不必在那邊等待。 .env: QUEUE_CONNECTION預設是sync 改成database:
Thumbnail
當伺服器需要處理一些比較花時間的任務時(如發送Email、上傳影片等等),讓user等待直到執行完畢,是個很不明智的選擇,這時候就很適合使用Queue,讓工作在背景執行,使用者就能立刻做下一件事,不必在那邊等待。 .env: QUEUE_CONNECTION預設是sync 改成database:
Thumbnail
Laravel Notifications(通知),是用來通知使用者應用程式訊息的功能,比如付款完成發送email或簡訊通知使用者,文章被訂閱通知等等。Notifications甚至還可以把通知訊息塞進DB,可以用來顯示在後台報表頁面中。 以下以發送email通知來舉例用法。 建立通知 發送通知
Thumbnail
Laravel Notifications(通知),是用來通知使用者應用程式訊息的功能,比如付款完成發送email或簡訊通知使用者,文章被訂閱通知等等。Notifications甚至還可以把通知訊息塞進DB,可以用來顯示在後台報表頁面中。 以下以發送email通知來舉例用法。 建立通知 發送通知
Thumbnail
在Laravel中想達到websocket效果,由後端主動傳訊給前端,需使用broadcasting 將event廣播出去,由前端來接收訊息。 因此在業界常看到使用Redis + socket.io的架構,也是本篇選擇的機制。 從伺服器廣播訊息到前端接收的流程,大概會是這樣: 安裝與設定Redis
Thumbnail
在Laravel中想達到websocket效果,由後端主動傳訊給前端,需使用broadcasting 將event廣播出去,由前端來接收訊息。 因此在業界常看到使用Redis + socket.io的架構,也是本篇選擇的機制。 從伺服器廣播訊息到前端接收的流程,大概會是這樣: 安裝與設定Redis
Thumbnail
假設資料如下: local DB裡面的test Collection SELECT SELECT可以這樣寫: 由於config/database.php中設定的default DB_CONNECTION是mysql,所以這邊特別指定使用mongodb connection。 回傳結果如下: 軟刪除
Thumbnail
假設資料如下: local DB裡面的test Collection SELECT SELECT可以這樣寫: 由於config/database.php中設定的default DB_CONNECTION是mysql,所以這邊特別指定使用mongodb connection。 回傳結果如下: 軟刪除
Thumbnail
如上篇,使用Migration來做DB版本控制,但是會發現開發過程中若是要建立測試資料,要進DB一筆一筆手動新增或執行預先寫好的insert sql,其實有點麻煩,使用Laravel提供的Seeder功能,就可以解決這個問題。 指令如下,假設建立一個user table seeder:
Thumbnail
如上篇,使用Migration來做DB版本控制,但是會發現開發過程中若是要建立測試資料,要進DB一筆一筆手動新增或執行預先寫好的insert sql,其實有點麻煩,使用Laravel提供的Seeder功能,就可以解決這個問題。 指令如下,假設建立一個user table seeder:
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News