當我們的系統觸發了一個延遲任務,Laravel會用當前的時間加上任務的延遲時間計算出任務的執行時間戳,然後將這個時間戳和任務信息序列化之後存入隊列,,Laravel 的隊列處理器會不斷查詢並執行隊列中滿足預計執行時間等於或早於當前時間的任務。
- 創建任務
我們通過 make:job 命令來創建一個任務:
php artian make:job CloseOrder
創建的任務類保存在 app/Jobs 目錄下,現在編輯剛剛創建的任務類:
app/Jobs/CloseOrder.php
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Models\Order; // 代表這個類需要被放到隊列中執行,而不是觸發時立即執行 class CloseOrder implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $order; public function __construct(Order $order, $delay) { $this->order = $order; // 設置延遲的時間,delay() 方法的參數代表多少秒之後執行 $this->delay($delay); } // 定義這個任務類具體的執行邏輯 // 當隊列處理器從隊列中取出任務時,會調用 handle() 方法 public function handle() { // 判斷對應的訂單是否已經被支付 // 如果已經支付則不需要關閉訂單,直接退出 if ($this->order->paid_at) { return; } // 通過事務執行 sql \DB::transaction(function() { // 將訂單的 closed 字段標記爲 true,即關閉訂單 $this->order->update(['closed' => true]); // 循環遍歷訂單中的商品 SKU,將訂單中的數量加回到 SKU 的庫存中去 foreach ($this->order->items as $item) { $item->productSku->addStock($item->amount); } }); } }
- 觸發任務
接下來我們需要在創建訂單之後觸發這個任務:app/Http/Controllers/OrdersController.php
use App\Jobs\CloseOrder; . . . public function store(Request $request) { . . . $this->dispatch(new CloseOrder($order, config('app.order_ttl'))); return $order; }
CloseOrder 構造函數的第二個參數延遲時間我們從配置文件中讀取,爲了方便我們測試,把這個值設置成 30 秒:
'order_ttl' => 30
- 測試
默認情況下,Laravel 生成的 .env 文件裏把隊列的驅動設置成了 sync(同步),在同步模式下延遲任務會被立即執行,所以需要先把隊列的驅動改成 redis:
.env
QUEUE_CONNECTION=redis
要使用 redis 作爲隊列驅動,我們還需要引入 predis/predis 這個包
composer require predis/predis
接下來啓動隊列處理器:
php artisan queue:work
切記:如果測試中途要修改定時任務執行程序的內容,必須先停止隊列,更改完事後重啓隊列。