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