本篇介紹如何在 Laravel 框架中,使用 Queue 處理非同步任務,並搭配 Laravel Horizon 進行視覺化監控管理。
為什麼需要 Queue?
在 Web 應用中,有些任務不需要即時完成,例如:
- 寄送 Email 通知
- 圖片壓縮、影片轉檔
- 呼叫第三方 API(如推播、簡訊)
- 大量資料報表產生
若這些任務在 HTTP 請求中同步執行,會造成使用者等待時間過長。透過 Queue,可以將任務丟到背景執行,讓使用者立即獲得回應。
什麼是 Laravel Horizon?
Laravel Horizon 是 Laravel 官方的 Queue 監控工具,提供美觀的 Dashboard 介面,可以即時查看:
- Queue 工作的執行狀況(待處理、執行中、失敗)
- 處理速率(jobs/min)
- 失敗任務的詳細錯誤訊息
- 各 Queue 的 Worker 數量與資源消耗
安裝 Laravel Horizon
# 安裝套件 composer require laravel/horizon # 發布設定與資源 php artisan horizon:install
執行後會產生:
config/horizon.php— Horizon 設定檔public/vendor/horizon— Dashboard 前端資源
設定 Queue Connection
Horizon 需要使用 Redis 作為 Queue Driver:
# .env
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
建立 Job
php artisan make:job SendWelcomeEmail
# app/Jobs/SendWelcomeEmail.php <?php namespace App\Jobs; use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Mail; class SendWelcomeEmail implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // 失敗重試次數 public int $tries = 3; // 逾時秒數 public int $timeout = 60; public function __construct( public User $user, ) {} public function handle(): void { Mail::to($this->user->email)->send( new \App\Mail\WelcomeMail($this->user) ); } // 失敗時的處理 public function failed(\Throwable $exception): void { logger()->error('SendWelcomeEmail failed', [ 'user_id' => $this->user->id, 'error' => $exception->getMessage(), ]); } }
派發 Job(Dispatch)
# Controller 中派發任務 use App\Jobs\SendWelcomeEmail; class UserController extends Controller { public function register(Request $request) { $user = User::create($request->validated()); // 立即丟入 Queue SendWelcomeEmail::dispatch($user); // 延遲 5 分鐘後執行 SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5)); // 指定丟到特定 Queue SendWelcomeEmail::dispatch($user)->onQueue('emails'); return response()->json(['message' => '註冊成功']); } }
設定 Horizon(config/horizon.php)
Horizon 的重點設定在於 environments,可以針對不同環境設定 Worker 數量與監控的 Queue:
# config/horizon.php 'environments' => [ 'production' => [ 'supervisor-1' => [ 'connection' => 'redis', 'queue' => ['default', 'emails', 'notifications'], 'balance' => 'auto', // 自動分配 Worker 'minProcesses' => 1, 'maxProcesses' => 10, 'tries' => 3, 'timeout' => 60, ], ], 'local' => [ 'supervisor-1' => [ 'connection' => 'redis', 'queue' => ['default', 'emails'], 'balance' => 'simple', 'processes' => 3, 'tries' => 1, ], ], ],
balance 策略說明
| 策略 | 說明 |
|---|---|
simple |
固定數量,平均分配到各 Queue |
auto |
依 Queue 積壓量自動動態調整 Worker 數 |
false |
不做 balance,按設定的 Queue 順序處理 |
啟動 Horizon
# 啟動 Horizon(包含所有 Queue Worker) php artisan horizon # 暫停所有 Worker(不停止進程) php artisan horizon:pause # 繼續執行 php artisan horizon:continue # 優雅終止(等待現有 Job 跑完後停止) php artisan horizon:terminate
使用 Supervisor 管理 Horizon
正式環境必須用 Supervisor 讓 Horizon 常駐,確保重啟後自動恢復:
# /etc/supervisor/conf.d/horizon.conf
[program:horizon]
command=php /var/www/html/artisan horizon
numprocs=1
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/horizon.log
stopwaitsecs=3600
supervisorctl reread supervisorctl update supervisorctl start horizon
注意:stopwaitsecs 建議設定比最長 Job timeout 還大,避免 Supervisor 強制 kill 掉正在執行中的 Job。
Horizon Dashboard 存取權限
預設 Horizon Dashboard 路由為 /horizon,在正式環境需要設定存取權限,避免公開:
# app/Providers/HorizonServiceProvider.php use Laravel\Horizon\Horizon; public function boot(): void { parent::boot(); Horizon::auth(function ($request) { // 只允許特定 Email 的使用者存取 return in_array($request->user()?->email, [ '[email protected]', ]); }); }
失敗任務處理
# 查看失敗任務清單 php artisan queue:failed # 重新執行指定失敗任務(by ID) php artisan queue:retry {id} # 重新執行所有失敗任務 php artisan queue:retry all # 清除所有失敗任務 php artisan queue:flush
Queue vs Horizon 差別
| 項目 | queue:work | Horizon |
|---|---|---|
| 監控介面 | 無 | 有(Web Dashboard) |
| 動態 Worker 調整 | 無 | 支援(auto balance) |
| Queue Driver | 任意(database, redis...) | 僅 Redis |
| 適合環境 | 小型專案、簡單需求 | 中大型專案、需監控 |
| 指標統計 | 無 | 有(throughput, runtime) |
小結
Laravel Queue 是處理耗時任務的標準解法,而 Laravel Horizon 則是讓你能夠「看見」Queue 運行狀況的利器。對於正式環境,強烈建議使用 Horizon + Supervisor 的組合,既有視覺化監控,也能保證服務的穩定性。
參考文獻: