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

安装
Laravel Horizon 要求使用Redisredis
来支持队列。因此,请确保在应用程序的config/queue.php
配置文件中已设置队列连接。
您可以使用 Composer 包管理器将 Horizon 安装到您的项目中:
1composer require laravel/horizon
安装 Horizon 后,使用horizon:install
Artisan 命令发布其资产:
1php artisan horizon:install
配置
发布 Horizon 的资源后,其主配置文件将位于config/horizon.php
。此配置文件允许您配置应用程序的队列工作器选项。每个配置选项都包含其用途的描述,因此请务必仔细研究此文件。
Horizon 使用内部命名的 Redis 连接horizon
。此 Redis 连接名称是保留的,不应在配置文件中将其分配给其他 Redis 连接,也不应将其作为配置文件中选项database.php
的值。use
horizon.php
环境
安装完成后,您应该熟悉的主要 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 个工作进程,并自动平衡分配给每个队列的工作进程数量。
您应该确保配置文件environments
的部分包含您计划运行 Horizon 的每个环境horizon
的条目。
主管
正如您在 Horizon 的默认配置文件中所见,每个环境可以包含一个或多个“主管”。默认情况下,配置文件将此主管定义为supervisor-1
;但是,您可以随意命名主管。每个主管本质上负责“监管”一组工作进程,并负责在队列之间平衡工作进程。
如果您想定义一组新的工作进程在给定环境中运行,可以向该环境添加额外的主管进程。如果您想为应用程序使用的给定队列定义不同的平衡策略或工作进程数量,也可以选择这样做。
维护模式
当你的应用程序处于维护模式时,排队的作业将不会被 Horizon 处理,除非在 Horizon 配置文件中force
定义了主管的选项:true
1'environments' => [2 'production' => [3 'supervisor-1' => [4 // ...5 'force' => true,6 ],7 ],8],
默认值
在 Horizon 的默认配置文件中,你会注意到一个defaults
配置选项。此配置选项指定了应用程序supervisor的默认值。supervisor 的默认配置值将合并到每个环境的 supervisor 配置中,从而避免在定义 supervisor 时进行不必要的重复配置。
平衡策略
与 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, [10 'taylor@laravel.com',11 ]);12 });13}
替代身份验证策略
请记住,Laravel 会自动将已验证的用户注入到 gate 闭包中。如果您的应用程序通过其他方式(例如 IP 限制)提供 Horizon 安全性,那么您的 Horizon 用户可能无需“登录”。因此,您需要将function (User $user)
上面的闭包签名更改为 ,function (User $user = null)
以强制 Laravel 不再要求身份验证。
沉默的工作
有时,您可能不想查看由应用程序或第三方软件包调度的某些作业。您可以将这些作业静默,以避免它们占用“已完成作业”列表中的空间。首先,将作业的类名添加到silenced
应用程序horizon
配置文件中的配置选项中:
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 进程并指示它使用horizon:pause
和horizon:continue
Artisan 命令继续处理作业:
1php artisan horizon:pause2 3php artisan horizon:continue
您还可以使用和Artisan 命令暂停和继续特定的 Horizon主管:horizon:pause-supervisor
horizon:continue-supervisor
1php artisan horizon:pause-supervisor supervisor-12 3php artisan horizon:continue-supervisor supervisor-1
您可以使用 Artisan 命令检查 Horizon 进程的当前状态horizon:status
:
1php artisan horizon:status
您可以使用Artisan 命令检查特定 Horizon主管的当前状态horizon:supervisor-status
:
1php artisan horizon:supervisor-status supervisor-1
您可以使用 Artisan 命令优雅地终止 Horizon 进程horizon:terminate
。所有当前正在处理的作业都将完成,然后 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 的配置文件通常存储在服务器的/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 配置并启动受监控的进程:
1sudo supervisorctl reread2 3sudo supervisorctl update4 5sudo supervisorctl start horizon
有关运行 Supervisor 的更多信息,请查阅Supervisor 文档。
标签
Horizon 允许您为作业分配“标签”,包括可邮寄邮件、广播事件、通知和队列事件监听器。实际上,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}
App\Models\Video
如果此作业与具有id
属性的实例一起排队1
,它将自动收到标签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
中调用这些方法:boot
App\Providers\HorizonServiceProvider
1/** 2 * Bootstrap any application services. 3 */ 4public function boot(): void 5{ 6 parent::boot(); 7 8 Horizon::routeSmsNotificationsTo('15556667777'); 9 Horizon::routeMailNotificationsTo('example@example.com');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 包含一个指标仪表盘,提供有关作业和队列等待时间以及吞吐量的信息。为了填充此仪表盘,您应该snapshot
在应用程序的routes/console.php
文件中将 Horizon 的 Artisan 命令配置为每五分钟运行一次:
1use Illuminate\Support\Facades\Schedule;2 3Schedule::command('horizon:snapshot')->everyFiveMinutes();
删除失败的作业
如果您想删除失败的作业,可以使用该horizon:forget
命令。该horizon:forget
命令接受失败作业的 ID 或 UUID 作为其唯一参数:
1php artisan horizon:forget 5
如果您想删除所有失败的作业,您可以--all
为horizon:forget
命令提供以下选项:
1php artisan horizon:forget --all
清除队列中的作业
如果您想要从应用程序的默认队列中删除所有作业,可以使用horizon:clear
Artisan 命令执行此操作:
1php artisan horizon:clear
您可以提供queue
从特定队列中删除作业的选项:
1php artisan horizon:clear --queue=emails