Laravel Horizon
简介
在深入了解 Laravel Horizon 之前,您应该熟悉 Laravel 的基本 队列服务。如果您还不熟悉 Laravel 提供的基本队列功能,Horizon 增强的 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
配置文件中配置好 supervisor 和 worker 后,您可以使用 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 supervisor。
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 部署到应用程序的实际服务器时,您应该配置一个进程监视器来监视 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 允许您将“标签”分配给作业,包括 mailables、广播事件、通知和排队事件侦听器。实际上,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 或 SMS 通知时,您应该查看相关通知渠道的先决条件。
如果您希望在您的一个队列有很长的等待时间时收到通知,您可以使用 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 包含一个指标仪表板,其中提供了有关您的作业和队列等待时间和吞吐量的信息。为了填充此仪表板,您应该配置 Horizon 的 snapshot
Artisan 命令,使其在应用程序的 routes/console.php
文件中每五分钟运行一次。
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