什麼是 Queue
在 Web 應用程式中,會有寄信、縮圖、對外部 API 查詢等需要數秒才能完成的處理。 若在 HTTP 請求中同步進行這些處理,使用者必須一直等到回應返回。 使用 Laravel 的 Queue,能將這些重負載處理在背景以非同步方式執行。 請求會立刻回應,實際處理由 worker process 另外執行。Queue 支援資料庫、Redis、Amazon SQS 等多種後端。
開發環境使用
sync driver 時,不透過 queue 即可立即執行 job。Queue 的設定
config/queue.php
Queue 的設定集中於config/queue.php。
以 QUEUE_CONNECTION 環境變數切換所使用的 driver。
.env 設定
資料庫 driver 的準備
使用database driver 時,需要儲存 job 的資料表。
Laravel 11 以後的新專案已預設包含 migration,
若沒有,可用以下指令建立:
Redis driver 的準備
使用redis driver 時,於 config/database.php 加入 Redis 連線設定,
並以 Composer 安裝 driver:
SQS Overflow Storage
Amazon SQS 的訊息 payload 有大小上限。 若要處理較大的 payload,可加入將超過的部分存到 cache store、只將 pointer 傳給 SQS 的設定。- 啟用
enabled時,會將大於 1MB 的 payload 存到指定的 cache store。 - 將
always設為true,則無論大小都會把所有 SQS payload 存到 cache store。 delete_after_processing會在 job 成功後刪除已儲存的 payload(預設true)。- 將
flush_on_clear設為true,執行queue:clear時會flushoverflow 用的 store。因會避免清掉一般 cache,建議搭配專用 store 使用。
建立 Job 類別
make:job 指令
以make:job Artisan 指令產生 job 類別的樣板:
app/Jobs/SendWelcomeEmail.php。
Job 類別的結構
ShouldQueue 介面,是在告訴 Laravel 這個 job 要以 queue 非同步處理。
Queueable trait 提供 job 佇列操作所需的方法。
Job 的分派
dispatch()
從 controller 或 service 將 job 送到 queue,使用dispatch():
延遲 dispatch
delay() 方法可讓 job 的執行延後指定時間。
dispatchAfterResponse()
使用dispatchAfterResponse() 會在回應 HTTP 給使用者之後立即執行 job。
在 sync driver 下也能運作,適合不需要專用 worker 的輕量用途。
分派到特定 queue
Queue Routing
若要將特定 job 類別預設導向指定 connection/queue,可在 ServiceProvider 的boot() 中使用 Queue::route()。無需為每個 job 類別加上 onQueue() / onConnection(),可集中管理。
Queue Routing 可被 job 端的
onQueue() / onConnection() 覆寫。同步執行(用於測試/開發)
使用dispatchSync() 會跳過 queue 立即執行。
大量 dispatch
當要一次分派多個獨立的 job 時,可使用Bus facade 的 bulk() 方法。適合不需要如 batch 處理般的追蹤或 callback 的情境。
Bus::bulk() 會依所設定的 queue connection 與 queue 名稱將 job 分組,並將每組整批推入 queue,因此效率較高。
Bus::bulk() 會將 job 以批次方式送入 queue。與 batch 處理(Bus::batch())不同,不提供進度追蹤或完成 callback。適合需要簡潔地批次送出大量獨立 job 的情境。Job 的處理
queue:work 指令
啟動 queue worker 來處理 job。Queue Worker 的監控選項
可組合常用選項精細控制 worker:在 Job 類別中設定重試
比起命令列選項,將設定寫在 job 類別本身有時更易於管理。Job 的 Release(Release middleware)
當在特定條件下想不執行 job 而放回 queue 時,使用Release middleware 可簡潔實作。
Release::unless() 會在條件為 false 時 release。
失敗 Job 的處理
準備 failed_jobs 資料表
當 job 超過最大嘗試次數,會記錄到failed_jobs 資料表。
若沒有此表,可用以下指令建立:
失敗時的收尾
在 job 定義failed() 方法,可撰寫失敗時的收尾處理。
依例外停止重試
有些例外類型希望不再重試而直接視為失敗。在bootstrap/app.php 的 withExceptions() 中以 dontRetry 指定要對應的例外類別。
dontRetryWhen。當 closure 回傳 true,job 會立即標記為失敗、不再重試。
檢視失敗 job 清單
重試失敗 job
刪除失敗 job
常用的 queue driver
database driver
無需額外的中介軟體即可使用的簡單 driver。 會將 job 存到jobs 資料表,由 worker 輪詢並處理。
- 優點:安裝簡單,可直接沿用既有 RDBMS
- 缺點:對資料庫負擔大,不適合大量 job
redis driver
在正式環境中最常使用的高速 driver。 以記憶體運作,吞吐量高於資料庫,可處理大量 job。- 優點:高速、可擴展
- 缺點:需要準備 Redis 伺服器
以 Supervisor 進行正式運行
在正式環境中,需要一種當queue:work process 因某原因停止時能自動重啟的機制。
Linux 環境中一般使用 Supervisor。
numprocs=2 平行啟動 2 個 worker process。
設定後重新載入 Supervisor:
實務範例:以 queue 處理寄信
1
建立 job 類別
2
實作 job 的處理
3
從 controller 分派
4
啟動 worker
總結
適合使用 queue 的時機
適合使用 queue 的時機
- 寄送 email/SMS
- 圖片、影片的縮圖或格式轉換
- 向外部 API 送出請求
- 產生報表或 CSV 匯出
- Webhook 的送出
開發時的訣竅
開發時的訣竅
將
.env 設為 QUEUE_CONNECTION=sync,job 就會不經過 queue 立即執行。
不用啟動 worker 就能確認運作,開發中很方便。常用指令總覽
常用指令總覽