Skip to content
全部文档

Laravel Horizon

简介

INFO

在深入了解 Laravel Horizon 之前,应先熟悉 Laravel 的基础队列服务。Horizon 为 Laravel 队列增加了额外功能,若不熟悉 Laravel 提供的基础队列特性,可能会感到困惑。

Laravel Horizon 为 Laravel 驱动的 Redis 队列 提供美观的仪表盘和代码驱动的配置。Horizon 让你轻松监控队列系统的关键指标,例如任务吞吐量、运行时间和失败任务。

使用 Horizon 时,所有队列 worker 配置都存储在单一、简洁的配置文件中。在版本控制的文件中定义 worker 配置,部署时可轻松扩展或修改队列 worker。

安装

WARNING

Laravel Horizon 要求使用 Redis 驱动队列。因此,请确保在应用的 config/queue.php 配置文件中将队列连接设为 redis

可通过 Composer 将 Horizon 安装到项目中:

shell
composer require laravel/horizon

安装 Horizon 后,使用 horizon:install Artisan 命令发布资源:

shell
php artisan horizon:install

配置

发布 Horizon 资源后,主配置文件位于 config/horizon.php。该文件用于配置应用的队列 worker 选项。每个配置选项都包含用途说明,请仔细阅读。

WARNING

Horizon 内部使用名为 horizon 的 Redis 连接。该连接名已保留,不应在 database.php 中分配给其他 Redis 连接,也不应作为 horizon.phpuse 选项的值。

环境

安装后,应首先熟悉 environments 配置选项。该选项是应用运行环境的数组,并为每个环境定义 worker 进程选项。默认包含 productionlocal 环境,也可按需添加更多环境:

'environments' => [
    'production' => [
        'supervisor-1' => [
            'maxProcesses' => 10,
            'balanceMaxShift' => 1,
            'balanceCooldown' => 3,
        ],
    ],

    'local' => [
        'supervisor-1' => [
            'maxProcesses' => 3,
        ],
    ],
],

还可定义通配符环境(*),在没有其他匹配环境时使用:

'environments' => [
    // ...

    '*' => [
        'supervisor-1' => [
            'maxProcesses' => 3,
        ],
    ],
],

启动 Horizon 时,会使用应用当前运行环境的 worker 进程配置。环境通常由 APP_ENV 环境变量 的值决定。例如,默认 local 环境配置为启动 3 个 worker 进程并自动平衡各队列的 worker 数量;默认 production 环境最多启动 10 个 worker 进程并自动平衡各队列的 worker 数量。

WARNING

请确保 horizon 配置文件的 environments 部分包含计划运行 Horizon 的每个环境的条目。

Supervisor

如 Horizon 默认配置文件所示,每个环境可包含一个或多个「supervisor」。默认配置将其定义为 supervisor-1,但你可以自由命名。每个 supervisor 本质上负责「监管」一组 worker 进程,并在各队列间平衡 worker 进程。

若要在某环境中定义新的 worker 进程组,可添加额外的 supervisor。例如,可为应用使用的特定队列定义不同的平衡策略或 worker 进程数量。

维护模式

应用处于维护模式时,Horizon 不会处理队列任务,除非在 Horizon 配置中将 supervisor 的 force 选项设为 true

'environments' => [
    'production' => [
        'supervisor-1' => [
            // ...
            'force' => true,
        ],
    ],
],

默认值

Horizon 默认配置中有 defaults 选项,用于指定应用 supervisor 的默认值。这些默认值会合并到各环境的 supervisor 配置中,避免重复定义。

平衡策略

与 Laravel 默认队列系统不同,Horizon 允许你从三种 worker 均衡策略中选择:simpleautofalsesimple 策略会在 worker 进程之间均匀分配传入的任务:

'balance' => 'simple',

auto 策略(配置文件的默认值)会根据队列当前负载调整每个队列的 worker 进程数量。例如,若 notifications 队列有 1000 个待处理任务而 render 队列为空,Horizon 会为 notifications 队列分配更多 worker,直到该队列清空。

使用 auto 策略时,你可以定义 minProcessesmaxProcesses 配置选项,以控制每个队列的最少进程数,以及 Horizon 整体应扩缩至的最大 worker 进程总数:

'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 策略)向队列分配更多 worker 进程。

balanceMaxShiftbalanceCooldown 配置值决定 Horizon 满足 worker 需求时的扩展速度。上述示例中,每 3 秒最多创建或销毁 1 个新进程。可根据应用需求调整这些值。

balance 选项设为 false 时,将使用 Laravel 的默认行为,即按配置中列出的顺序处理队列。

仪表盘授权

Horizon 仪表盘可通过 /horizon 路由访问。默认情况下,仅在 local 环境可访问。但在 app/Providers/HorizonServiceProvider.php 中有授权 Gate 定义,用于控制非 local 环境下对 Horizon 的访问。可按需修改此 Gate 以限制访问:

php
/**
 * 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, [
            'taylor@laravel.com',
        ]);
    });
}

替代认证策略

请记住,Laravel 会自动将已认证用户注入 Gate 闭包。若应用通过 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 进程:

shell
php artisan horizon

可使用 horizon:pausehorizon:continue Artisan 命令暂停 Horizon 进程并指示其继续处理任务:

shell
php artisan horizon:pause

php artisan horizon:continue

也可使用 horizon:pause-supervisorhorizon:continue-supervisor Artisan 命令暂停和恢复特定 Horizon supervisor

shell
php artisan horizon:pause-supervisor supervisor-1

php artisan horizon:continue-supervisor supervisor-1

可使用 horizon:status Artisan 命令检查 Horizon 进程当前状态:

shell
php artisan horizon:status

可使用 horizon:supervisor-status Artisan 命令检查特定 Horizon supervisor 的当前状态:

shell
php artisan horizon:supervisor-status supervisor-1

可使用 horizon:terminate Artisan 命令优雅终止 Horizon 进程。当前正在处理的任务会完成后,Horizon 才会停止执行:

shell
php artisan horizon:terminate

部署 Horizon

准备将 Horizon 部署到应用实际服务器时,应配置进程监控器监控 php artisan horizon 命令,并在意外退出时重启。下面会介绍如何安装进程监控器。

部署过程中,应指示 Horizon 进程终止,以便进程监控器重启并加载新代码:

shell
php artisan horizon:terminate

安装 Supervisor

Supervisor 是 Linux 操作系统的进程监控器,会在 horizon 进程停止执行时自动重启。在 Ubuntu 上可使用以下命令安装。若使用其他系统,通常可通过系统包管理器安装:

shell
sudo apt-get install supervisor

INFO

若自行配置 Supervisor 感到复杂,可考虑使用 Laravel Forge,它会为你的 Laravel 项目自动安装并配置 Supervisor。

Supervisor 配置

Supervisor 配置文件通常位于服务器的 /etc/supervisor/conf.d 目录。可在此目录创建任意数量的配置文件,指示 supervisor 如何监控进程。例如,创建 horizon.conf 以启动并监控 horizon 进程:

ini
[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 配置并启动受监控的进程:

shell
sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start horizon

INFO

有关运行 Supervisor 的更多信息,请参阅 Supervisor 文档

标签

Horizon 允许为任务分配「标签」,包括 mailable、广播事件、通知和队列事件监听器。Horizon 会根据任务附带的 Eloquent 模型智能自动标记大多数任务。例如,查看以下任务:

php
<?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
    {
        // ...
    }
}

若此任务与 id1App\Models\Video 实例一起入队,将自动获得标签 App\Models\Video:1。Horizon 会搜索任务属性中的 Eloquent 模型,若找到则使用模型类名和主键智能标记任务:

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];
    }
}

通知

WARNING

配置 Horizon 发送 Slack 或 SMS 通知时,请查阅相关通知通道的先决条件

若希望在某个队列等待时间过长时收到通知,可使用 Horizon::routeMailNotificationsToHorizon::routeSlackNotificationsToHorizon::routeSmsNotificationsTo 方法。可在 App\Providers\HorizonServiceProviderboot 方法中调用:

php
/**
 * Bootstrap any application services.
 */
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 命令每 5 分钟运行一次:

use Illuminate\Support\Facades\Schedule;

Schedule::command('horizon:snapshot')->everyFiveMinutes();

删除失败任务

若要删除失败任务,可使用 horizon:forget 命令。该命令接受失败任务的 ID 或 UUID 作为唯一参数:

shell
php artisan horizon:forget 5

若要删除所有失败任务,可向 horizon:forget 命令提供 --all 选项:

shell
php artisan horizon:forget --all

清空队列中的任务

若要删除应用默认队列中的所有任务,可使用 horizon:clear Artisan 命令:

shell
php artisan horizon:clear

可提供 queue 选项以删除特定队列中的任务:

shell
php artisan horizon:clear --queue=emails