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

安装 
WARNING
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。此配置文件允许您配置应用程序的队列工作者选项。每个配置选项都包含其目的的描述,因此请务必彻底探索此文件。
WARNING
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,
        ],
    ],
],当您启动 Horizon 时,它将使用应用程序运行的环境的工作者进程配置选项。通常,环境由 APP_ENV 环境变量 的值确定。例如,默认的 local Horizon 环境配置为启动三个工作者进程,并自动平衡分配给每个队列的工作者进程数量。默认的 production 环境配置为启动最多 10 个工作者进程,并自动平衡分配给每个队列的工作者进程数量。
WARNING
您应该确保 horizon 配置文件的 environments 部分包含您计划运行 Horizon 的每个环境的条目。
监督者 
正如您在 Horizon 的默认配置文件中看到的,每个环境可以包含一个或多个“监督者”。默认情况下,配置文件将此监督者定义为 supervisor-1;但是,您可以随意命名您的监督者。每个监督者本质上负责“监督”一组工作者进程,并负责在队列之间平衡工作者进程。
如果您希望为应用程序使用的某个队列定义不同的平衡策略或工作者进程计数,您可以为给定环境添加额外的监督者。
维护模式 
当您的应用程序处于维护模式时,除非在 Horizon 配置文件中将监督者的 force 选项定义为 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 安装的访问:
/**
 * 注册 Horizon 门。
 *
 * 此门决定谁可以在非本地环境中访问 Horizon。
 */
protected function gate(): void
{
    Gate::define('viewHorizon', function (User $user) {
        return in_array($user->email, [
            'taylor@laravel.com',
        ]);
    });
}替代身份验证策略 
请记住,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 Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    // ...
}升级 Horizon 
在升级到 Horizon 的新主要版本时,重要的是仔细查看升级指南。此外,在升级到任何新的 Horizon 版本时,您应该重新发布 Horizon 的资产:
php artisan horizon:publish为了保持资产的最新状态并避免将来更新中的问题,您可以将 vendor:publish --tag=laravel-assets 命令添加到应用程序的 composer.json 文件中的 post-update-cmd 脚本中:
{
  "scripts": {
    "post-update-cmd": [
      "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
    ]
  }
}运行 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: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 supervisorNOTE
如果自己配置 Supervisor 听起来很复杂,可以考虑使用 Laravel Forge,它会自动为您的 Laravel 项目安装和配置 Supervisor。
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 horizonNOTE
有关运行 Supervisor 的更多信息,请查阅 Supervisor 文档。
标签 
Horizon 允许您为作业分配“标签”,包括可邮寄的、广播事件、通知和排队的事件监听器。实际上,Horizon 会根据附加到作业的 Eloquent 模型智能地自动标记大多数作业。例如,看看以下作业:
<?php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class RenderVideo implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    /**
     * 创建一个新的作业实例。
     */
    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('example@example.com');
    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 命令通过应用程序的调度器每五分钟运行一次:
/**
 * 定义应用程序的命令计划。
 */
protected function schedule(Schedule $schedule): void
{
    $schedule->command('horizon:snapshot')->everyFiveMinutes();
}删除失败的作业 
如果您希望删除失败的作业,可以使用 horizon:forget 命令。horizon:forget 命令接受失败作业的 ID 或 UUID 作为其唯一参数:
php artisan horizon:forget 5清除队列中的作业 
如果您希望从应用程序的默认队列中删除所有作业,可以使用 horizon:clear Artisan 命令:
php artisan horizon:clear您可以提供 queue 选项以从特定队列中删除作业:
php artisan horizon:clear --queue=emails