Horizon
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
Laravel Horizon
簡介
[!NOTE] 在深入研究 Laravel Horizon 之前,您應該先熟悉 Laravel 的基礎佇列服務。Horizon 在 Laravel 的佇列基礎上增加了額外功能,如果您還不熟悉 Laravel 提供的基本佇列功能,這些功能可能會讓您感到困惑。
Laravel Horizon 為您的 Laravel 驅動的 Redis 佇列提供了一個美麗的儀表板和程式碼驅動的設定。Horizon 讓您可以輕鬆監控佇列系統的關鍵指標,例如任務吞吐量、運行時間和任務失敗。
使用 Horizon 時,所有佇列 Worker 設定都儲存在一個簡單的設定檔中。透過在版本控制的檔案中定義應用程式的 Worker 設定,您可以在部署應用程式時輕鬆地擴展或修改應用程式的佇列 Worker。
安裝
[!WARNING] Laravel Horizon 要求您使用 Redis 來驅動您的佇列。因此,您應該確保應用程式的
config/queue.php設定檔中的佇列連接設為redis。Horizon 目前不相容於 Redis Cluster。
您可以使用 Composer 套件管理器將 Horizon 安裝到您的專案中:
composer require laravel/horizon
安裝 Horizon 後,使用 horizon:install Artisan 指令來發佈其資源:
php artisan horizon:install
設定
發佈 Horizon 的資源後,其主要設定檔將位於 config/horizon.php。此設定檔允許您配置應用程式的佇列 Worker 選項。每個設定選項都包含其用途的說明,因此請務必仔細探索此檔案。
[!WARNING] Horizon 在內部使用一個名為
horizon的 Redis 連接。此 Redis 連接名稱是保留的,不應在database.php設定檔中將其分配給另一個 Redis 連接,也不應作為horizon.php設定檔中use選項的值。
環境
安裝後,您應該熟悉的主要 Horizon 設定選項是 environments 設定選項。此設定選項是您的應用程式運行的環境陣列,並定義每個環境的 Worker 進程選項。預設情況下,此條目包含 production 和 local 環境。然而,您可以根據需要自由新增更多環境:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
您還可以定義一個通配符環境(*),當找不到其他匹配的環境時將使用它:
'environments' => [
// ...
'*' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
當您啟動 Horizon 時,它將使用您的應用程式運行的環境的 Worker 進程設定選項。通常,環境由 APP_ENV 環境變數的值決定。例如,預設的 local Horizon 環境被設定為啟動三個 Worker 進程,並自動平衡分配給每個佇列的 Worker 進程數量。預設的 production 環境被設定為最多啟動 10 個 Worker 進程,並自動平衡分配給每個佇列的 Worker 進程數量。
[!WARNING] 您應該確保
horizon設定檔的environments部分包含您計劃運行 Horizon 的每個環境的條目。
Supervisors
正如您在 Horizon 的預設設定檔中看到的,每個環境可以包含一個或多個「supervisors」。預設情況下,設定檔將此 supervisor 定義為 supervisor-1;然而,您可以自由地為您的 supervisors 命名。每個 supervisor 本質上負責「監督」一組 Worker 進程,並負責在佇列之間平衡 Worker 進程。
如果您想要定義一個應該在該環境中運行的新 Worker 進程群組,您可以向給定環境新增額外的 supervisors。如果您想要為應用程式使用的給定佇列定義不同的平衡策略或 Worker 進程數量,您可以選擇這樣做。
維護模式
當您的應用程式處於維護模式時,除非在 Horizon 設定檔中將 supervisor 的 force 選項定義為 true,否則佇列任務將不會被 Horizon 處理:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'force' => true,
],
],
],
預設值
在 Horizon 的預設設定檔中,您會注意到一個 defaults 設定選項。此設定選項指定應用程式 supervisors 的預設值。Supervisor 的預設設定值將合併到每個環境的 supervisor 設定中,讓您在定義 supervisors 時避免不必要的重複。
儀表板授權
可以透過 /horizon 路由存取 Horizon 儀表板。預設情況下,您只能在 local 環境中存取此儀表板。然而,在您的 app/Providers/HorizonServiceProvider.php 檔案中,有一個授權閘道定義。此授權閘道控制在非本地環境中對 Horizon 的存取。您可以根據需要自由修改此閘道來限制對 Horizon 安裝的存取:
/**
* 註冊 Horizon 閘道。
*
* 此閘道決定誰可以在非本地環境中存取 Horizon。
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
替代認證策略
請記住,Laravel 會自動將已認證的使用者注入到閘道閉包中。如果您的應用程式透過另一種方法(例如 IP 限制)提供 Horizon 安全性,那麼您的 Horizon 使用者可能不需要「登入」。因此,您需要將上面的 function (User $user) 閉包簽章更改為 function (User $user = null),以強制 Laravel 不要求認證。
最大任務嘗試次數
[!NOTE] 在調整這些選項之前,請確保您熟悉 Laravel 的預設佇列服務和「嘗試」的概念。
您可以在 supervisor 的設定中定義任務可以消耗的最大嘗試次數:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'tries' => 10,
],
],
],
[!NOTE] 此選項類似於使用 Artisan 指令處理佇列時的
--tries選項。
使用 WithoutOverlapping 或 RateLimited 等中介層時,調整 tries 選項至關重要,因為它們會消耗嘗試次數。要處理此問題,請在 supervisor 級別調整 tries 設定值,或在任務類別上定義 $tries 屬性。
如果您不設定 tries 選項,Horizon 預設為單次嘗試,除非任務類別定義了 $tries,它優先於 Horizon 設定。
將 tries 或 $tries 設為 0 允許無限嘗試,這在嘗試次數不確定時非常理想。為了防止無止盡的失敗,您可以透過在任務類別上設定 $maxExceptions 屬性來限制允許的例外數量。
任務逾時
同樣地,您可以在 supervisor 級別設定 timeout 值,指定 Worker 進程可以運行任務多少秒後才會被強制終止。一旦終止,任務將根據您的佇列設定被重試或標記為失敗:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'timeout' => 60,
],
],
],
[!WARNING] 使用
auto平衡策略時,Horizon 會將進行中的 Worker 視為「掛起」,並在縮容時在 Horizon 逾時後強制終止它們。始終確保 Horizon 逾時大於任何任務級別的逾時,否則任務可能在執行中被終止。此外,timeout值應始終比config/queue.php設定檔中定義的retry_after值短至少幾秒鐘。否則,您的任務可能會被處理兩次。
任務退避
您可以在 supervisor 級別定義 backoff 值,指定 Horizon 在重試遇到未處理例外的任務之前應等待多久:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'backoff' => 10,
],
],
],
您還可以使用 backoff 值的陣列來設定「指數」退避。在此範例中,第一次重試的延遲為 1 秒,第二次為 5 秒,第三次為 10 秒,如果有更多剩餘嘗試,則每次後續重試為 10 秒:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'backoff' => [1, 5, 10],
],
],
],
靜音任務
有時,您可能對檢視應用程式或第三方套件分派的某些任務不感興趣。與其讓這些任務佔用「已完成任務」列表中的空間,您可以將它們靜音。要開始,請將任務的類別名稱新增到應用程式 horizon 設定檔的 silenced 設定選項中:
'silenced' => [
App\Jobs\ProcessPodcast::class,
],
除了靜音單個任務類別外,Horizon 還支援基於標籤靜音任務。如果您想要隱藏共享共同標籤的多個任務,這非常有用:
'silenced_tags' => [
'notifications'
],
或者,您想要靜音的任務可以實現 Laravel\Horizon\Contracts\Silenced 介面。如果任務實現了此介面,即使它不在 silenced 設定陣列中,它也會自動被靜音:
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
use Queueable;
// ...
}
平衡策略
每個 supervisor 可以處理一個或多個佇列,但與 Laravel 的預設佇列系統不同,Horizon 允許您從三種 Worker 平衡策略中選擇:auto、simple 和 false。
自動平衡
auto 策略(也是預設策略)會根據佇列的當前工作量來調整每個佇列的 Worker 進程數量。例如,如果您的 notifications 佇列有 1,000 個待處理任務,而您的 default 佇列是空的,Horizon 將分配更多 Worker 到您的 notifications 佇列,直到該佇列清空。
使用 auto 策略時,您還可以設定 minProcesses 和 maxProcesses 設定選項:
minProcesses定義每個佇列的最小 Worker 進程數。此值必須大於或等於 1。maxProcesses定義 Horizon 可以在所有佇列之間擴展到的最大 Worker 進程總數。此值通常應大於佇列數量乘以minProcesses值。為了防止 supervisor 產生任何進程,您可以將此值設為 0。
例如,您可以設定 Horizon 在每個佇列上至少維護一個進程,並最多擴展到 10 個 Worker 進程:
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default', 'notifications'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
],
autoScalingStrategy 設定選項決定 Horizon 如何為佇列分配更多 Worker 進程。您可以選擇兩種策略之一:
time策略將根據清除佇列所需的總預估時間來分配 Worker。size策略將根據佇列上的任務總數來分配 Worker。
balanceMaxShift 和 balanceCooldown 設定值決定 Horizon 擴展以滿足 Worker 需求的速度。在上面的範例中,每三秒最多會建立或銷毀一個新進程。您可以根據應用程式的需求自由調整這些值。
佇列優先順序與自動平衡
使用 auto 平衡策略時,Horizon 不會在佇列之間實施嚴格的優先順序。佇列在 supervisor 設定中的順序不會影響 Worker 進程的分配方式。相反,Horizon 依賴所選的 autoScalingStrategy 來根據佇列負載動態分配 Worker 進程。
例如,在以下設定中,high 佇列並沒有優先於 default 佇列,儘管它出現在列表的第一位:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['high', 'default'],
'minProcesses' => 1,
'maxProcesses' => 10,
],
],
],
如果您需要在佇列之間實施相對優先順序,您可以定義多個 supervisors 並明確分配處理資源:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default'],
'minProcesses' => 1,
'maxProcesses' => 10,
],
'supervisor-2' => [
// ...
'queue' => ['images'],
'minProcesses' => 1,
'maxProcesses' => 1,
],
],
],
在此範例中,預設 queue 可以擴展到 10 個進程,而 images 佇列限制為一個進程。此設定確保您的佇列可以獨立擴展。
[!NOTE] 分派資源密集型任務時,最好將它們分配到一個具有有限
maxProcesses值的專用佇列。否則,這些任務可能會消耗過多的 CPU 資源並使您的系統過載。
簡單平衡
simple 策略將 Worker 進程均勻分配到指定的佇列中。使用此策略時,Horizon 不會自動擴展 Worker 進程數量。相反,它使用固定數量的進程:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default', 'notifications'],
'balance' => 'simple',
'processes' => 10,
],
],
],
在上面的範例中,Horizon 將為每個佇列分配 5 個進程,將總共 10 個均勻分配。
如果您想要單獨控制分配給每個佇列的 Worker 進程數量,您可以定義多個 supervisors:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default'],
'balance' => 'simple',
'processes' => 10,
],
'supervisor-notifications' => [
// ...
'queue' => ['notifications'],
'balance' => 'simple',
'processes' => 2,
],
],
],
使用此設定,Horizon 將為 default 佇列分配 10 個進程,為 notifications 佇列分配 2 個進程。
無平衡
當 balance 選項設為 false 時,Horizon 會嚴格按照佇列列出的順序處理它們,類似於 Laravel 的預設佇列系統。然而,如果任務開始累積,它仍然會擴展 Worker 進程數量:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default', 'notifications'],
'balance' => false,
'minProcesses' => 1,
'maxProcesses' => 10,
],
],
],
在上面的範例中,default 佇列中的任務始終優先於 notifications 佇列中的任務。例如,如果 default 中有 1,000 個任務,而 notifications 中只有 10 個,Horizon 將在處理任何來自 notifications 的任務之前完全處理所有 default 任務。
您可以使用 minProcesses 和 maxProcesses 選項來控制 Horizon 擴展 Worker 進程的能力:
minProcesses定義 Worker 進程的最小總數。此值必須大於或等於 1。maxProcesses定義 Horizon 可以擴展到的最大 Worker 進程總數。
升級 Horizon
升級到新的主要版本 Horizon 時,請務必仔細查閱升級指南。
運行 Horizon
在應用程式的 config/horizon.php 設定檔中配置好 supervisors 和 workers 後,您可以使用 horizon Artisan 指令啟動 Horizon。此單一指令將啟動當前環境的所有已設定 Worker 進程:
php artisan horizon
您可以使用 horizon:pause 和 horizon:continue Artisan 指令來暫停 Horizon 進程並指示它繼續處理任務:
php artisan horizon:pause
php artisan horizon:continue
您還可以使用 horizon:pause-supervisor 和 horizon:continue-supervisor Artisan 指令來暫停和繼續特定的 Horizon supervisors:
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue-supervisor supervisor-1
您可以使用 horizon:status Artisan 指令來檢查 Horizon 進程的當前狀態:
php artisan horizon:status
您可以使用 horizon:supervisor-status Artisan 指令來檢查特定 Horizon supervisor 的當前狀態:
php artisan horizon:supervisor-status supervisor-1
您可以使用 horizon:terminate Artisan 指令優雅地終止 Horizon 進程。當前正在處理的任何任務將被完成,然後 Horizon 將停止執行:
php artisan horizon:terminate
自動重新啟動 Horizon
在本地開發期間,您可以運行 horizon:listen 指令。使用 horizon:listen 指令時,您不需要在想要重新載入更新的程式碼時手動重新啟動 Horizon。使用此功能之前,您應該確保 Node 已安裝在您的本地開發環境中。此外,您應該在專案中安裝 Chokidar 檔案監聽庫:
npm install --save-dev chokidar
Chokidar 安裝後,您可以使用 horizon:listen 指令啟動 Horizon:
php artisan horizon:listen
在 Docker 或 Vagrant 中運行時,您應該使用 --poll 選項:
php artisan horizon:listen --poll
您可以使用應用程式 config/horizon.php 設定檔中的 watch 設定選項來設定應被監聽的目錄和檔案:
'watch' => [
'app',
'bootstrap',
'config',
'database',
'public/**/*.php',
'resources/**/*.php',
'routes',
'composer.lock',
'.env',
],
部署 Horizon
當您準備好將 Horizon 部署到應用程式的實際伺服器時,您應該設定一個進程監控器來監控 php artisan horizon 指令,並在其意外退出時重新啟動它。不用擔心,我們將在下面討論如何安裝進程監控器。
在應用程式的部署過程中,您應該指示 Horizon 進程終止,以便它將被您的進程監控器重新啟動並接收您的程式碼變更:
php artisan horizon:terminate
安裝 Supervisor
Supervisor 是 Linux 作業系統的進程監控器,如果 horizon 進程停止執行,它將自動重新啟動它。要在 Ubuntu 上安裝 Supervisor,您可以使用以下指令。如果您不使用 Ubuntu,您可以像往常一樣使用作業系統的套件管理器安裝 Supervisor:
sudo apt-get install supervisor
[!NOTE] 如果自己設定 Supervisor 聽起來很麻煩,請考慮使用 Laravel Cloud,它可以為您的 Laravel 應用程式管理背景進程。
Supervisor 設定
Supervisor 設定檔通常儲存在伺服器的 /etc/supervisor/conf.d 目錄中。在此目錄中,您可以建立任意數量的設定檔來指示 supervisor 如何監控您的進程。例如,讓我們建立一個 horizon.conf 檔案來啟動和監控 horizon 進程:
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600
定義 Supervisor 設定時,您應該確保 stopwaitsecs 的值大於您運行時間最長的任務消耗的秒數。否則,Supervisor 可能在任務完成處理之前就將其殺死。
[!WARNING] 雖然上面的範例對基於 Ubuntu 的伺服器有效,但其他伺服器作業系統上 Supervisor 設定檔的位置和副檔名可能有所不同。請查閱伺服器的文件了解更多資訊。
啟動 Supervisor
建立設定檔後,您可以使用以下指令更新 Supervisor 設定並啟動被監控的進程:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
[!NOTE] 有關運行 Supervisor 的更多資訊,請參閱 Supervisor 文件。
標籤
Horizon 允許您為任務分配「標籤」,包括郵件、廣播事件、通知和佇列事件監聽器。事實上,Horizon 會根據附加到任務的 Eloquent 模型來智能地自動標記大多數任務。例如,看看以下任務:
<?php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class RenderVideo implements ShouldQueue
{
use Queueable;
/**
* 建立新的任務實例。
*/
public function __construct(
public Video $video,
) {}
/**
* 執行任務。
*/
public function handle(): void
{
// ...
}
}
如果此任務與一個 id 屬性為 1 的 App\Models\Video 實例一起被佇列化,它將自動接收標籤 App\Models\Video:1。這是因為 Horizon 將搜尋任務的屬性以查找任何 Eloquent 模型。如果找到 Eloquent 模型,Horizon 將使用模型的類別名稱和主鍵來智能地標記任務:
use App\Jobs\RenderVideo;
use App\Models\Video;
$video = Video::find(1);
RenderVideo::dispatch($video);
手動標記任務
如果您想要手動定義可佇列化物件之一的標籤,您可以在類別上定義一個 tags 方法:
class RenderVideo implements ShouldQueue
{
/**
* 取得應分配給任務的標籤。
*
* @return array<int, string>
*/
public function tags(): array
{
return ['render', 'video:'.$this->video->id];
}
}
手動標記事件監聽器
檢索佇列事件監聽器的標籤時,Horizon 將自動將事件實例傳遞給 tags 方法,允許您將事件資料新增到標籤中:
class SendRenderNotifications implements ShouldQueue
{
/**
* 取得應分配給監聽器的標籤。
*
* @return array<int, string>
*/
public function tags(VideoRendered $event): array
{
return ['video:'.$event->video->id];
}
}
通知
[!WARNING] 設定 Horizon 發送 Slack 或 SMS 通知時,您應該查閱相關通知通道的先決條件。
如果您想要在其中一個佇列等待時間過長時收到通知,您可以使用 Horizon::routeMailNotificationsTo、Horizon::routeSlackNotificationsTo 和 Horizon::routeSmsNotificationsTo 方法。您可以從應用程式的 App\Providers\HorizonServiceProvider 的 boot 方法中呼叫這些方法:
/**
* 引導任何應用程式服務。
*/
public function boot(): void
{
parent::boot();
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('[email protected]');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
設定通知等待時間閾值
您可以在應用程式的 config/horizon.php 設定檔中設定多少秒被視為「長時間等待」。此檔案中的 waits 設定選項允許您控制每個連接 / 佇列組合的長時間等待閾值。任何未定義的連接 / 佇列組合將預設為 60 秒的長時間等待閾值:
'waits' => [
'redis:critical' => 30,
'redis:default' => 60,
'redis:batch' => 120,
],
將佇列的閾值設為 0 將停用該佇列的長時間等待通知。
指標
Horizon 包含一個指標儀表板,提供有關任務和佇列等待時間及吞吐量的資訊。為了填充此儀表板,您應該設定 Horizon 的 snapshot Artisan 指令在應用程式的 routes/console.php 檔案中每五分鐘執行一次:
use Illuminate\Support\Facades\Schedule;
Schedule::command('horizon:snapshot')->everyFiveMinutes();
如果您想要刪除所有指標資料,您可以呼叫 horizon:clear-metrics Artisan 指令:
php artisan horizon:clear-metrics
刪除失敗任務
如果您想要刪除失敗任務,您可以使用 horizon:forget 指令。horizon:forget 指令接受失敗任務的 ID 或 UUID 作為其唯一參數:
php artisan horizon:forget 5
如果您想要刪除所有失敗任務,您可以向 horizon:forget 指令提供 --all 選項:
php artisan horizon:forget --all
清除佇列中的任務
如果您想要從應用程式的預設佇列中刪除所有任務,您可以使用 horizon:clear Artisan 指令來實現:
php artisan horizon:clear
您可以提供 queue 選項來從特定佇列中刪除任務:
php artisan horizon:clear --queue=emails