laravel
10. 软件包
Laravel Horizon

介绍

[!NOTE]
在深入研究 Laravel Horizon 之前,您应该熟悉 Laravel 的基础队列服务。如果您还不熟悉 Laravel 提供的基本队列功能,那么 Horizon 为 Laravel 队列增加的额外功能可能会让您感到困惑。

Laravel Horizon (opens in a new tab) 为您的 Laravel 驱动的Redis 队列提供了一个漂亮的仪表盘和代码驱动的配置。Horizon 允许您轻松监控队列系统的关键指标,如作业吞吐量、运行时和作业失败情况。

使用 Horizon 时,您所有的队列工作者配置都存储在一个简单的配置文件中。通过在版本控制的文件中定义应用程序的工作者配置,您可以在部署应用程序时轻松地扩展或修改应用程序的队列工作者。

安装

[!WARNING]
Laravel Horizon 要求您使用Redis (opens in a new tab) 为您的队列提供动力。因此,您应该确保在应用程序的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,
        ],
    ],
],

您还可以定义一个通配符环境(*),当找不到其他匹配环境时将使用该环境:

'environments' => [
    //...

    '*' => [
        '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(通知)队列有 1000 个待处理任务,而您的 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 Queueable;
 
    //...
}

升级 Horizon

当升级到 Horizon 的新主要版本时,您应该仔细查看升级指南 (opens in a new tab)

运行 Horizon

在应用程序的 config/horizon.php 配置文件中配置了监督器和工作进程后,您可以使用 horizon Artisan 命令启动 Horizon。此单个命令将为当前环境启动所有配置的工作进程:

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 监督器

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 supervisor

[!NOTE]
如果自己配置 Supervisor 听起来很困难,可以考虑使用Laravel Forge (opens in a new tab),它将为您的 Laravel 项目自动安装和配置 Supervisor。

监督器配置

监督器配置文件通常存储在服务器的/etc/supervisor/conf.d目录中。在该目录内,您可以创建任意数量的配置文件,以指示监督器如何监控您的进程。例如,我们创建一个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

在定义监督器配置时,您应确保stopwaitsecs的值大于您最长运行作业所消耗的秒数。否则,监督器可能会在作业完成处理之前将其终止。

[!WARNING]
虽然上述示例对基于 Ubuntu 的服务器有效,但监督器配置文件的预期位置和文件扩展名可能因其他服务器操作系统而异。请查阅您的服务器文档以获取更多信息。

启动监督器

创建配置文件后,您可以使用以下命令更新监督器配置并启动受监控的进程:

sudo supervisorctl reread
 
sudo supervisorctl update
 
sudo supervisorctl start horizon

[!NOTE]
有关运行监督器的更多信息,请查阅监督器文档 (opens in a new tab)

标签

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;

    /**
     * 创建一个新的作业实例。
     */
    public function __construct(
        public Video $video,
    ) {}

    /**
     * 执行作业。
     */
    public function handle(): void
    {
        //...
    }
}

如果此作业与具有id属性为1App\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::routeMailNotificationsToHorizon::routeSlackNotificationsToHorizon::routeSmsNotificationsTo方法。您可以在应用程序的App\Providers\HorizonServiceProviderboot方法中调用这些方法:

/**
 * 引导任何应用程序服务。
 */
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 包含一个指标仪表板,可提供有关您的作业和队列等待时间以及吞吐量的信息。为了填充此仪表板,您应该在应用程序的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