如何在 Laravel 中逐步暫停和控制隊列

最後更新: 5月2025
  • Laravel 提供了一個統一的佇列 API,支援多種驅動程式(資料庫、Redis、SQS 等),並將連接和邏輯佇列分開。
  • 作業功能允許將繁重的任務移至後台運行,並支援序列化模型、中間件、一次性作業、加密、批次和字串。
  • 使用 queue:work、Supervisor 和 queue:pause/queue:continue 等指令來管理工作進程,控制重試、逾時和優先權。
  • 有一個完整的故障、監控和測試生態系統(failed_jobs、queue:retry、queue:monitor、Queue::fake),使生產環境中的佇列可靠。

Laravel 中的隊列管理

在 Laravel 中,對於任何稍微嚴肅一點的項目來說,使用隊列幾乎是必不可少的:發送電子郵件、處理 CSV 文件、生成 PDF、調整圖像大小、調用外部 API……如果所有這些操作都在主 HTTP 請求中完成,網站就會變慢,出現 500 錯誤,用戶最終只能絕望地盯著空白屏幕。

好消息是,Laravel 提供了一個強大而靈活的佇列基礎架構,允許你將繁重的任務委託給後台進程,按優先順序管理它們,將它們分組,監控它們,在失敗時重試,甚至可以暫時暫停工作進程進行維護。讓我們仔細看看(不遺漏任何細節),如何充分利用它,包括如何在生產環境中暫停、恢復和控制其行為。

基本概念:連接、佇列和驅動程式

在 Laravel 中,暫停任何操作之前,你需要了解以下兩者之間的差異: 隊列連接和隊列本身。 在 config/queue.php 你有一個數組 connections 在這裡定義每個後端: 資料庫、Redis、Beanstalkd、SQS、同步、空值、故障轉移、RabbitMQ (如果您添加的話),等等。

每個連線可以有多個 邏輯隊列 (例如 default, emails, high, low, imports該屬性 queue 每個連線都指示如果沒有指定其他佇列時,作業將進入哪個預設佇列。 onQueue()這樣你就可以做到以下幾點:

  • 快速排隊 用於電子郵件或短時交易。
  • 另一個低優先權項目 適用於處理影片或大型報告等長時間任務。
  • 按任務類型劃分的佇列: imports, notificaciones, reportes等等。

當你啟動 worker 時 php artisan queue:work預設情況下,它監聽指定的佇列。 默認 在選定的連線上。如果需要設定優先權,可以傳遞多個以逗號分隔的佇列:

php artisan queue:work redis --queue=high,default,low

這樣,工作進程會先處理高優先級任務,然後處理預設任務,最後處理低優先級任務,這在系統負載較高時至關重要,因為你不希望一個 30 分鐘的報告阻塞高優先級電子郵件。

佇列驅動因素和先決條件

Laravel 為多個佇列驅動程式提供了統一的 API ,但每個驅動程式都有其特定的功能和相依性。在討論暫停工作進程之前,選擇合適的驅動程式至關重要。

驅動程式資料庫

司機 數據庫 它將作業儲存在 SQL 表中(通常 jobs它易於設置,非常適合小型專案或開發環境,在這些環境中,您不想使用 Redis 或 SQS。

基本步驟:

  • 產生遷移: php artisan queue:table (或在新版本中,它以以下形式出現) create_jobs_table 有缺陷的)。
  • 運行遷移: php artisan migrate.
  • 配置 隊列連線=資料庫 在 .env 並確認 config/queue.php 這 'default' => env('QUEUE_CONNECTION','database').

它既方便又足夠強大,但是當隊列很多、流量很大時,它可能會成為瓶頸,因為所有資料都透過同一個關聯式資料庫傳輸。

驅動程式 Redis

Redis驅動程式是生產環境中的標準主力驅動程式。它速度快,支援高級功能(鎖定、速率限制、原子鎖、Horizo​​​​n 等),並且可擴展性非常好。

要點:

  • 您需要在以下位置設定 Redis 連線: config/database.php.
  • 選項 serializer y compression 來自 Redis 它們不兼容 使用佇列驅動程式。
  • 如果您使用 Redis集群佇列名稱必須包含井號(#),例如: {default}這樣,佇列中的所有鍵都會落入同一個插槽。
  • 選項 block_for 它允許工作進程在等待任務時阻塞幾秒鐘,而不是持續輪詢: 節省 CPU 並減少對 Redis 伺服器的查詢。

例如:

'redis' => ,

如果你把 block_for = 0如果希望它能快速回應諸如此類的訊號,那麼工人將一直處於阻塞狀態,直到有工作空缺為止。 SIGTERM 或暫停命令。

其他驅動程式:SQS、Beanstalkd、null、故障轉移…

除了資料庫和 Redis 之外,Laravel 還支援其他系統:

  • 亞馬遜SQSAWS 託管佇列,並支援 FIFO郵件分組、去重和進階可見性設定。需要 SDK。 aws/aws-sdk-php.
  • 豆莖使用該軟體包非常快速方便 pda/pheanstalk.
  • 同步執行以下任務 立即 在相同的流程中。在開發或測試中很有用,但在生產環境中,它實際上相當於完全沒有使用佇列。
  • 空:一旦任務被“調度”,就立即將其丟棄。用於完全禁用隊列系統。
  • 故障轉移這允許您配置多個連接,如果主連接失敗,Laravel 會嘗試將任務推送到清單中的下一個連接。這非常適合以下環境: 高可用性 至關重要。
  專業網頁設計:打造高轉換率網站的完整指南

在暫停和復原工作流程的層面上,無論驅動程式是什麼,邏輯實際上都是相同的,因為它是由 Artisan 命令和Worker類別管理的,而不是由佇列後端管理的。

作業創建和模型序列化

Un 約伯記‬ 在 Laravel 中,它只是一個封裝了待執行任務的類,該任務會被放入佇列中。它通常使用 Artisan 生成並保存。 app/Jobs.

要創建一個:

php artisan make:job ProcessPodcast

產生的類別 實施 ShouldQueue 並且通常使用特徵 Dispatchable, InteractsWithQueue, Queueable y SerializesModels關鍵在於方法。 handle()這就是工人接到工作後要做的事。

一個特別強大的特性是Eloquent 模型的自動序列化。如果在 Job 建構函式中接受一個模型,Laravel 不會將整個物件加入佇列中,而只會加入其識別碼。當任務被處理時,它會從資料庫重建模型,並載入其關聯關係。

典型範例(以下使用偽代碼,避免逐字重複):

  • 建造者收到 播客 $podcast.
  • 該特徵 SerializesModels 它負責保存ID。
  • En handle(AudioProcessor $processor) 你負責完成繁重的工作(例如處理音訊)。

如果您不希望模型中的關係被序列化(這樣有效負載就不會很大),您可以使用 $model->withoutRelations() 或在 PHP 8 中,該屬性 # 在開發商推廣的房產上。

處理方法中的依賴注入

另一個優點是您可以 直接將服務注入處理方法中 (就像控制器一樣),因為 Laravel 的 Jobs 服務也使用了服務容器。例如,您可以要求一個 AudioProcessorHTTP 用戶端、儲存庫等,框架會負責解析它們。

如果您需要對所謂的…進行超精細控制 handle你可以用 Container::bindMethod() 在服務提供者中,您可以自己定義注入邏輯,但在大多數情況下,沒有必要把事情搞得這麼複雜。

作業中介軟體

就像路由有中間件一樣,作業也可以有中間件。 特定中介軟體 在作業執行前後運行。這大大減少了方法中的重複程式碼。 handle() 它尤其適用於:

  • 限速 使用 Redis。
  • 避免 重疊 涉及同一資源的作品。
  • 常規例外 適用於不穩定的 API。
  • 在特定條件下跳過工作(Skip 中介軟體).

中介軟體通常放置在 app/Jobs/Middleware 並從該方法返回。 middleware() 出自約伯記。概念範例:

public function middleware(): array { return ;

特殊類型的作業:獨一無二、加密且鎖定。

Laravel 可讓您將某些工作標記為 獨特 確保永遠不會有兩個相同的實例同時排隊。這是透過實作該介面來實現的。 ShouldBeUnique o ShouldBeUniqueUntilProcessing並支援定義密鑰 uniqueId() 以及一段時間 uniqueFor.

這非常適合以下情況:

  • 重新計算產品的搜尋索引: 每個產品僅限一次 直到一切結束。
  • 與不允許對相同資源同時要求的外部服務進行同步。

Laravel 底層使用了一種 原子鎖 快取(Redis、memcached、資料庫等)。您也可以為該鎖選擇快取驅動程式。 uniqueVia().

此外,還有接口。 ShouldBeEncrypted,你以此表明完整的工作是 進入佇列前請輸入您的程式碼在傳遞敏感資料時非常有用,因為你不希望資料以明文形式透過 Redis、SQS 或資料庫傳輸。

以不同模式調度、延遲和執行作業

一旦你定義了工作,就可以啟動它了。 Job::dispatch() 可以從控制器、事件監聽器、Artisan 指令,甚至是閉包傳遞參數。 dispatch 他們直接去找約伯的建造者。

有趣的變體:

  • dispatchIf() y dispatchUnless() 將貨物整理成一行。
  • delay() 因此,該作業必須等到特定日期/時間才能處理。
  • dispatchAfterResponse() o afterResponse() en dispatch() 閉包會將執行延遲到 HTTP 回應傳送完畢之後,但仍在同一個 PHP 進程內執行。
  • dispatchSync() 同步運行作業(不經過佇列),但重複使用同一個類別。
  • 特殊聯繫,例如 deferred o background 在回應之後執行,可以在同一進程中執行,也可以在新的 PHP 進程中執行。

最後,您可以使用鍊式方法指定連接和佇列。 onConnection() y onQueue()無論是在調度時還是在作業建構器中。

精細控制:重試、退避、逾時和錯誤處理

在 Laravel 中使用佇列的一個重要部分是決定重試任務的次數、兩次嘗試之間的等待時間以及何時將其視為失敗。

  OpenCode 完全指南:如何使用 ChatGPT、Claude 和 Gemini 進行 AI 編程

可用選項:

  • --tries en queue:work 設定該工作進程所有任務的最大嘗試次數(除非任務本身定義了最大嘗試次數)。 $tries o tries()).
  • $tries 或者一種方法 tries() 在作業類中,用於逐作業控制。
  • retryUntil() 意思是:「你可以隨意嘗試,直到某個時間點,之後就會失敗。」如果它與 tries, 命令 retryUntil.
  • $maxExceptions 當問題是由於反覆未處理的異常引起的時,應終止程序。
  • $timeout o --timeout 指示作業在工作進程終止子進程之前可以執行多少秒。
  • $failOnTimeout 如果逾時,則將作業標記為失敗。
  • $backoff 方法 backoff() 定義重試之間的等待時間,即使數值呈指數級變化。

如果某個任務拋出了一個未捕獲的異常,Laravel 將會… 釋放回尾部 直到配置的嘗試次數用盡為止。您可以透過以下方式手動控制此操作: release($segundos) o fail($exception) 當你想故意將一項任務標記為失敗。

失敗的作業、failed_jobs 表和清理

當約伯竭盡全力之後,他就會被認為 失敗的工作 Laravel 會將其插入到表中。 failed_jobs (或如果您配置了 DynamoDB,則為 DynamoDB)。此表儲存 UUID、連線、佇列、作業負載以及拋出的例外狀況。

有用的命令:

  • php artisan queue:failed 列出失敗的案例。
  • php artisan queue:retry <id|all> 再試一次。
  • php artisan queue:forget <id> 刪除一個。
  • php artisan queue:flush 全部刪除(新版本中可選擇按小時刪除)。
  • php artisan queue:prune-failed --hours=48 自動刪除舊記錄。

在 Jobs 中,您可以定義一個方法 failed(Throwable $exception) 為了進行額外的清理工作:通知使用者、撤銷部分操作、寫入專用日誌等。請注意,這將建立一個新的作業實例來呼叫。 failed()因此,不要依賴房產變更 handle().

作業批次:將作業批次處理並用字串將它們組合起來

當您需要啟動數百或數千個相關作業時(例如,分塊匯入大型 CSV 檔案、批次處理文件、清理資料等),建議使用Laravel 的批次功能。

基本流程:

  • 建立表 job_batches 同 php artisan queue:batches-table && php artisan migrate.
  • 標記將屬於批次的作業,並賦予其特性。 Batchable.
  • 使用 Bus::batch() 發送一組帶有回呼的作業 then, catch y finally.
  • 透過以下方式從每個作業存取批次 $this->batch() 並查看是否已取消。
  • 可以在正在進行的批次中新增更多作業 $this->batch()->add().

批次與鍊式操作完美契合:你可以在批次中包含鍊式操作,也可以將批次作為更大鍊式操作的元素。當你希望多個鍊式操作「並行」運行,並在所有鍊式操作完成後觸發特定操作時,這種整合方式尤其有用。

工作執行、優先順序和部署

隊列的所有奇妙之處都源自於你擁有 一名或多名工人 運行命令 queue:work (o queue:listen(儘管後者效率較低)。這些進程是長時間運行的守護進程,它們監聽各自的佇列。

關鍵選項 queue:work:

  • 連接: php artisan queue:work redis, queue:work database等等。
  • 可樂: --queue=high,default,low 確定優先次序。
  • -十一:處理單一作業並退出。
  • –max-jobs:處理 X 個作業並終止(以便定期重新啟動工作進程並釋放記憶體)。
  • –最大時間:運轉 N 秒後退出。
  • -睡覺:當沒有工作機會時等待的秒數。
  • ——空時停止:清空佇列並乾淨俐落地關閉(非常適合 Docker 環境或一次性作業)。
  • -暫停如前所述,每個作業的超時時間(秒)。
  • -力即使應用程式處於維護模式,也要處理作業。

由於這些進程需要長時間運行,因此在生產環境中,幾乎總是使用類似 Supervisor(在 Linux 系統上)的進程監控工具來啟動 X 個工作進程,在進程崩潰時重新啟動它們,並管理日誌。一個典型的 Laravel Supervisor 配置區塊定義了:

  • 命令 與 php artisan queue:work ... 具體的。
  • numprocs 指示要並行啟動多少個工作進程。
  • 日誌路徑、使用者、自動重新啟動等。

Laravel 中的隊列暫停和恢復

接下來我們來談談維護中經常很重要的一個問題:如何在不突然停止任務、不留下未完成任務的情況下暫停佇列中的工作進程。 Laravel 提供了幾種機制來實現這一點,有些是較新的,有些是傳統的。

暫停:佇列:暫停和中斷訊號

在最新版本中,Laravel 新增了用於暫停和恢復worker 執行的特定命令:

  • php artisan queue:pause:告訴系統停止接受新任務,但讓正在進行的任務完成。
  • php artisan queue:continue:從中斷的地方繼續處理新任務。
  掌握軟體單元測試

這些命令透過 Laravel 內部使用的快取機制向工作進程發送訊號。因此,即使你的實際快取佇列使用的是其他類型的緩存,正確配置快取驅動程式(例如 memcached、redis、資料庫、檔案等)也至關重要。

重要提示:暫停佇列或工作進程不會取消或刪除已排隊的作業;它只是阻止在暫停期間從佇列中移除更多作業。

調查中斷情況及其對性能的影響

每次 worker 完成任務後,Laravel 都會檢查快取中是否有重啟、暫停或繼續的訊號。這雖然開銷很小,但確實存在。如果您非常注重效能,並且確定不需要透過命令暫停或重新啟動 worker,則可以透過呼叫下列程式碼來停用此輪詢:

Queue::withoutInterruptionPolling();

甚至可以調整靜態屬性 Illuminate\Queue\Worker 停用特定觸摸(可重啟, 可暫停但是,如果你這樣做,然後嘗試使用 queue:pause o queue:restart工人們 他們不會發現的。這是一個需要仔細考慮的設計決策。

使用 Supervisor 或 Docker 手動暫停

除了 Laravel 自身的命令之外,在生產環境中,進程通常也會以間接的方式「暫停」:

  • 停止主管程序 (supervisorctl stop laravel-worker:*),這會導致工人死亡或停止工作。
  • 運用 ——空時停止 在部署過程中,讓工作進程完成佇列中的任務,然後關閉進程。
  • 在 Docker 中,讓容器進程與 --stop-when-empty 然後它就關機了。

與...的區別 queue:pause 完全停止進程後,作業將停止處理,直到工作進程重新啟動。 內部暫停 你可以將進程保存在記憶體中,等待命令執行。 continue這對於快速回滾部署或短期維護非常有用。

資料庫事務和提交後事件

一個典型的錯誤:在資料庫事務中調度任務。如果不小心,工作進程可能會在提交作業完成之前執行任務,導致你期望的模型或資料尚未存在於資料庫中。

為了避免這種尷尬局面,你有兩個選擇:

  • 激活 'after_commit' => true 在佇列連線配置中,任何在交易中分派的作業都會在內部被延遲,直到已確認為止。
  • 使用依作業方法 afterCommit() o beforeCommit() 調度時,強制僅在特定作業中執行此行為。

如果事務被撤銷,則事務期間分派的作業將被丟棄,而不會進入佇列,這正是為了保持一致性通常所希望的。

監控、隊列清理和測試

一旦佇列真正開始運行,僅僅發布作業然後置之不理是不夠的;你應該監控和測試一切的運行情況。

實用工具和命令:

  • queue:clear 清空特定佇列(SQS、Redis、資料庫)。
  • queue:monitor 監控特定佇列中的作業數量並觸發事件 QueueBusy 如果它們超過閾值。
  • 招募活動 如 Queue::before, Queue::after, Queue::looping y Queue::failing 掛鉤自訂邏輯(日誌、指標、回溯等)。
  • 測試中的假貨 運用 Queue::fake() y Bus::fake() 驗證作業、字串或批次是否已分派,而無需實際執行它們。

在測驗中,您可以聲明“此作業已排隊 X 次”、“包含這些作業的鏈已按此順序調度”或“沒有特定作業排隊”,這為複雜的流程提供了很大的安全性。

整個生態系統——包括 Redis 或 SQS 等驅動程式、精心設計的作業、用於微調的中間件、批次、鍊式呼叫、重試配置、Supervisor 管理的 worker 以及諸如此類的命令—— queue:pause y queue:continue— Laravel 讓您能夠精確到毫米地掌控後台處理,從典型的電子郵件發送到具有故障轉移的分散式佇列架構,確保您的應用程式在 HTTP 層繼續快速回應,同時在底層執行繁重的工作,而不會阻塞使用者。

Laravel 框架
相關文章:
強大的 Laravel 框架