Laravel Horizon
简介
在深入了解 Laravel Horizon 之前,您应该熟悉 Laravel 的基本队列服务。如果您还不熟悉 Laravel 提供的基本队列功能,Horizon 增强的 Laravel 队列的附加功能可能会让您感到困惑。
Laravel Horizon 为您的 Laravel 驱动的 Redis 队列提供了一个美观的仪表盘和代码驱动的配置。 Horizon 允许您轻松监控队列系统的关键指标,例如任务吞吐量、运行时和任务失败。
当使用 Horizon 时,您的所有队列工作进程配置都存储在一个简单的配置文件中。通过在版本控制文件中定义应用程序的工作进程配置,您可以在部署应用程序时轻松扩展或修改应用程序的队列工作进程。

安装
Laravel Horizon 要求您使用 Redis 来驱动您的队列。因此,您应该确保您的队列连接在应用程序的 config/queue.php
配置文件中设置为 redis
。
您可以使用 Composer 包管理器将 Horizon 安装到您的项目中
1composer require laravel/horizon
安装 Horizon 后,使用 horizon:install
Artisan 命令发布其资源
1php artisan horizon:install
配置
发布 Horizon 的资源后,其主要配置文件将位于 config/horizon.php
。此配置文件允许您为您的应用程序配置队列工作进程选项。每个配置选项都包含对其用途的描述,因此请务必彻底浏览此文件。
Horizon 在内部使用名为 horizon
的 Redis 连接。此 Redis 连接名称是保留的,不应在 database.php
配置文件中或作为 horizon.php
配置文件中 use
选项的值分配给另一个 Redis 连接。
环境
安装后,您应该熟悉的主要 Horizon 配置选项是 environments
配置选项。此配置选项是应用程序运行的环境数组,并为每个环境定义工作进程选项。默认情况下,此条目包含 production
和 local
环境。但是,您可以根据需要自由添加更多环境
1'environments' => [ 2 'production' => [ 3 'supervisor-1' => [ 4 'maxProcesses' => 10, 5 'balanceMaxShift' => 1, 6 'balanceCooldown' => 3, 7 ], 8 ], 9 10 'local' => [11 'supervisor-1' => [12 'maxProcesses' => 3,13 ],14 ],15],
您还可以定义通配符环境 (*
),当找不到其他匹配环境时将使用该环境
1'environments' => [2 // ...3 4 '*' => [5 'supervisor-1' => [6 'maxProcesses' => 3,7 ],8 ],9],
当您启动 Horizon 时,它将使用应用程序正在运行的环境的工作进程配置选项。通常,环境由 APP_ENV
环境变量的值确定。例如,默认的 local
Horizon 环境配置为启动三个工作进程,并自动平衡分配给每个队列的工作进程数量。默认的 production
环境配置为最多启动 10 个工作进程,并自动平衡分配给每个队列的工作进程数量。
您应确保您的 horizon
配置文件的 environments
部分包含您计划在其上运行 Horizon 的每个环境的条目。
监控器
正如您在 Horizon 的默认配置文件中看到的那样,每个环境可以包含一个或多个“监控器”。默认情况下,配置文件将此监控器定义为 supervisor-1
;但是,您可以自由地命名您的监控器。每个监控器本质上负责“监控”一组工作进程,并负责平衡跨队列的工作进程。
如果您想定义应该在该环境中运行的一组新的工作进程,您可以向给定环境添加其他监控器。如果您想为应用程序使用的给定队列定义不同的平衡策略或工作进程计数,您可以选择这样做。
维护模式
当您的应用程序处于维护模式时,除非监控器的 force
选项在 Horizon 配置文件中定义为 true
,否则队列中的任务将不会被 Horizon 处理
1'environments' => [2 'production' => [3 'supervisor-1' => [4 // ...5 'force' => true,6 ],7 ],8],
默认值
在 Horizon 的默认配置文件中,您会注意到一个 defaults
配置选项。此配置选项指定应用程序监控器的默认值。监控器的默认配置值将合并到每个环境的监控器配置中,从而允许您在定义监控器时避免不必要的重复。
平衡策略
与 Laravel 的默认队列系统不同,Horizon 允许您从三种工作进程平衡策略中进行选择:simple
、auto
和 false
。 simple
策略在工作进程之间平均分配传入的任务
1'balance' => 'simple',
auto
策略是配置文件的默认策略,它根据队列的当前工作负载调整每个队列的工作进程数量。例如,如果您的 notifications
队列有 1,000 个待处理任务,而您的 render
队列为空,Horizon 将为您的 notifications
队列分配更多工作进程,直到队列为空。
当使用 auto
策略时,您可以定义 minProcesses
和 maxProcesses
配置选项来控制每个队列的最小进程数和 Horizon 应总共扩展和缩减的最大工作进程数
1'environments' => [ 2 'production' => [ 3 'supervisor-1' => [ 4 'connection' => 'redis', 5 'queue' => ['default'], 6 'balance' => 'auto', 7 'autoScalingStrategy' => 'time', 8 'minProcesses' => 1, 9 'maxProcesses' => 10,10 'balanceMaxShift' => 1,11 'balanceCooldown' => 3,12 'tries' => 3,13 ],14 ],15],
autoScalingStrategy
配置值确定 Horizon 是否将基于清除队列所需的总时间(time
策略)或队列上的任务总数(size
策略)为队列分配更多工作进程。
balanceMaxShift
和 balanceCooldown
配置值确定 Horizon 扩展以满足工作进程需求的速度。在上面的示例中,每三秒钟最多创建一个或销毁一个新进程。您可以根据应用程序的需要自由调整这些值。
当 balance
选项设置为 false
时,将使用默认的 Laravel 行为,其中队列按照它们在配置中列出的顺序处理。
仪表盘授权
可以通过 /horizon
路由访问 Horizon 仪表盘。默认情况下,您只能在 local
环境中访问此仪表盘。但是,在您的 app/Providers/HorizonServiceProvider.php
文件中,有一个授权门卫定义。此授权门卫控制对非本地环境中 Horizon 的访问。您可以根据需要自由修改此门卫以限制对 Horizon 安装的访问
1/** 2 * Register the Horizon gate. 3 * 4 * This gate determines who can access Horizon in non-local environments. 5 */ 6protected function gate(): void 7{ 8 Gate::define('viewHorizon', function (User $user) { 9 return in_array($user->email, [11 ]);12 });13}
其他身份验证策略
请记住,Laravel 会自动将经过身份验证的用户注入到门卫闭包中。如果您的应用程序通过另一种方法(例如 IP 限制)提供 Horizon 安全性,那么您的 Horizon 用户可能不需要“登录”。因此,您需要将上面的 function (User $user)
闭包签名更改为 function (User $user = null)
,以强制 Laravel 不要求身份验证。
静默任务
有时,您可能对查看应用程序或第三方软件包分发的某些任务不感兴趣。您可以将这些任务静音,而不是让这些任务占用“已完成任务”列表中的空间。要开始使用,请将任务的类名添加到应用程序的 horizon
配置文件中的 silenced
配置选项中
1'silenced' => [2 App\Jobs\ProcessPodcast::class,3],
或者,您希望静音的任务可以实现 Laravel\Horizon\Contracts\Silenced
接口。如果任务实现了此接口,即使它不存在于 silenced
配置数组中,它也会自动静音
1use Laravel\Horizon\Contracts\Silenced;2 3class ProcessPodcast implements ShouldQueue, Silenced4{5 use Queueable;6 7 // ...8}
升级 Horizon
当升级到新的 Horizon 主要版本时,仔细查看升级指南非常重要。
运行 Horizon
在您的应用程序的 config/horizon.php
配置文件中配置了监控器和工作进程后,您可以使用 horizon
Artisan 命令启动 Horizon。此单个命令将启动当前环境的所有已配置工作进程
1php artisan horizon
您可以使用 horizon:pause
和 horizon:continue
Artisan 命令暂停 Horizon 进程并指示其继续处理任务
1php artisan horizon:pause2 3php artisan horizon:continue
您还可以使用 horizon:pause-supervisor
和 horizon:continue-supervisor
Artisan 命令暂停和继续特定的 Horizon 监控器
1php artisan horizon:pause-supervisor supervisor-12 3php artisan horizon:continue-supervisor supervisor-1
您可以使用 horizon:status
Artisan 命令检查 Horizon 进程的当前状态
1php artisan horizon:status
您可以使用 horizon:supervisor-status
Artisan 命令检查特定 Horizon 监控器的当前状态
1php artisan horizon:supervisor-status supervisor-1
您可以使用 horizon:terminate
Artisan 命令优雅地终止 Horizon 进程。任何当前正在处理的任务都将完成,然后 Horizon 将停止执行
1php artisan horizon:terminate
部署 Horizon
当您准备将 Horizon 部署到应用程序的实际服务器时,您应该配置一个进程监控器来监控 php artisan horizon
命令,并在它意外退出时重新启动它。别担心,我们将在下面讨论如何安装进程监控器。
在应用程序的部署过程中,您应该指示 Horizon 进程终止,以便它将被您的进程监控器重新启动并接收您的代码更改
1php artisan horizon:terminate
安装 Supervisor
Supervisor 是 Linux 操作系统的进程监控器,如果 horizon
进程停止执行,它将自动重新启动它。要在 Ubuntu 上安装 Supervisor,您可以使用以下命令。如果您没有使用 Ubuntu,您很可能可以使用操作系统的软件包管理器安装 Supervisor
1sudo apt-get install supervisor
如果您觉得自己配置 Supervisor 让人感到不知所措,请考虑使用 Laravel Cloud,它可以为您的 Laravel 应用程序管理后台进程。
Supervisor 配置
Supervisor 配置文件通常存储在服务器的 /etc/supervisor/conf.d
目录中。在此目录中,您可以创建任意数量的配置文件,这些文件指示 supervisor 如何监控您的进程。例如,让我们创建一个 horizon.conf
文件,该文件启动并监控 horizon
进程
1[program:horizon]2process_name=%(program_name)s3command=php /home/forge/example.com/artisan horizon4autostart=true5autorestart=true6user=forge7redirect_stderr=true8stdout_logfile=/home/forge/example.com/horizon.log9stopwaitsecs=3600
在定义 Supervisor 配置时,您应确保 stopwaitsecs
的值大于最长运行任务所消耗的秒数。否则,Supervisor 可能会在任务完成处理之前将其杀死。
虽然上面的示例对于基于 Ubuntu 的服务器有效,但 Supervisor 配置文件预期的位置和文件扩展名可能因其他服务器操作系统而异。请查阅服务器的文档以获取更多信息。
启动 Supervisor
创建配置文件后,您可以更新 Supervisor 配置并使用以下命令启动受监控的进程
1sudo supervisorctl reread2 3sudo supervisorctl update4 5sudo supervisorctl start horizon
有关运行 Supervisor 的更多信息,请查阅 Supervisor 文档。
标签
Horizon 允许您为任务分配“标签”,包括 mailables、广播事件、通知和排队的事件监听器。实际上,Horizon 将根据附加到任务的 Eloquent 模型智能地自动标记大多数任务。例如,看一下以下任务
1<?php 2 3namespace App\Jobs; 4 5use App\Models\Video; 6use Illuminate\Contracts\Queue\ShouldQueue; 7use Illuminate\Foundation\Queue\Queueable; 8 9class RenderVideo implements ShouldQueue10{11 use Queueable;12 13 /**14 * Create a new job instance.15 */16 public function __construct(17 public Video $video,18 ) {}19 20 /**21 * Execute the job.22 */23 public function handle(): void24 {25 // ...26 }27}
如果此任务使用 id
属性为 1
的 App\Models\Video
实例排队,它将自动收到标签 App\Models\Video:1
。这是因为 Horizon 将在任务的属性中搜索任何 Eloquent 模型。如果找到 Eloquent 模型,Horizon 将使用模型的类名和主键智能地标记任务
1use App\Jobs\RenderVideo;2use App\Models\Video;3 4$video = Video::find(1);5 6RenderVideo::dispatch($video);
手动标记任务
如果您想手动定义队列对象的标签,您可以在类上定义一个 tags
方法
1class RenderVideo implements ShouldQueue 2{ 3 /** 4 * Get the tags that should be assigned to the job. 5 * 6 * @return array<int, string> 7 */ 8 public function tags(): array 9 {10 return ['render', 'video:'.$this->video->id];11 }12}
手动标记事件监听器
当检索排队的事件监听器的标签时,Horizon 将自动将事件实例传递给 tags
方法,允许您将事件数据添加到标签
1class SendRenderNotifications implements ShouldQueue 2{ 3 /** 4 * Get the tags that should be assigned to the listener. 5 * 6 * @return array<int, string> 7 */ 8 public function tags(VideoRendered $event): array 9 {10 return ['video:'.$event->video->id];11 }12}
通知
当配置 Horizon 发送 Slack 或 SMS 通知时,您应该查看相关通知渠道的先决条件。
如果您希望在您的一个队列等待时间过长时收到通知,您可以使用 Horizon::routeMailNotificationsTo
、Horizon::routeSlackNotificationsTo
和 Horizon::routeSmsNotificationsTo
方法。您可以从应用程序的 App\Providers\HorizonServiceProvider
的 boot
方法中调用这些方法
1/** 2 * Bootstrap any application services. 3 */ 4public function boot(): void 5{ 6 parent::boot(); 7 8 Horizon::routeSmsNotificationsTo('15556667777');10 Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');11}
配置通知等待时间阈值
您可以在应用程序的 config/horizon.php
配置文件中配置多少秒被认为是“长等待”。此文件中的 waits
配置选项允许您控制每个连接/队列组合的长等待阈值。任何未定义的连接/队列组合都将默认为 60 秒的长等待阈值
1'waits' => [2 'redis:critical' => 30,3 'redis:default' => 60,4 'redis:batch' => 120,5],
指标
Horizon 包括一个指标仪表盘,该仪表盘提供有关您的任务和队列等待时间以及吞吐量的信息。为了填充此仪表盘,您应该配置 Horizon 的 snapshot
Artisan 命令,使其每五分钟在应用程序的 routes/console.php
文件中运行一次
1use Illuminate\Support\Facades\Schedule;2 3Schedule::command('horizon:snapshot')->everyFiveMinutes();
删除失败的任务
如果您想删除失败的任务,您可以使用 horizon:forget
命令。 horizon:forget
命令接受失败任务的 ID 或 UUID 作为其唯一参数
1php artisan horizon:forget 5
如果您想删除所有失败的任务,您可以为 horizon:forget
命令提供 --all
选项
1php artisan horizon:forget --all
从队列中清除任务
如果您想从应用程序的默认队列中删除所有任务,您可以使用 horizon:clear
Artisan 命令
1php artisan horizon:clear
您可以提供 queue
选项以从特定队列中删除任务
1php artisan horizon:clear --queue=emails