0
user-people-family-house-home
>

【Laravel】Queue + Horizon 完整實作 - 非同步任務與監控

本篇介紹如何在 Laravel 框架中,使用 Queue 處理非同步任務,並搭配 Laravel Horizon 進行視...

Posted by Roy on 2026-02-26 17:40:17
1 目前 1 人正在閱讀
|
| Views

本篇介紹如何在 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 的組合,既有視覺化監控,也能保證服務的穩定性。

參考文獻:

https://laravel.com/docs/11.x/queues

https://laravel.com/docs/11.x/horizon

留言區

請先登入才能發表留言