Horizon 队列管理工具
Laravel Horizon
介绍
技巧:在深入研究 Laravel Horizon 之前,你应该熟悉 Laravel 的基础队列服务。Horizon 通过其他功能来增强 Laravel 的队列能力,如果你不熟悉 Laravel 提供的基本队列功能,这可能会让你感到困惑。
Laravel Horizon 为你的 Laravel Redis 队列 提供了一个漂亮的控制面板和代码驱动的配置。它可以方便的监控队列系统的关键指标,如任务吞吐量、运行时间、任务失败。
当你使用 Horizon 时,所有的队列 worker 配置存储在一个简单的配置文件中。通过结合版本控制,你可以定义修改应用程序的工作程序配置,实现在部署应用程序时轻松扩展或修改应用程序的队列工作程序。
安装
注意:Laravel Horizon 要求你使用 Redis 为队列提供服务。因此,你需要确保在你的应用程序配置文件
config/queue.php
中的redis
项已经正确设置。
你可以使用 Composer 将 Horization 安装到你的 Laravel 项目里:
composer require laravel/horizon
Horizon 安装之后,使用 Artisan 命令 horizon:install
发布资源:
php artisan horizon:install
配置
Horizon 资源发布之后,主要的配置文件会被分配到 config/horizon.php
文件。通过这个配置文件,你可以为应用程序配置队列 worker 选项。每个配置选项都包含其用途的描述,因此要仔细研究这个文件。
环境
安装完成后,你应该熟悉的主要 Horizon 配置选项是 environments
选项。这个配置选项定义了应用程序运行的一系列环境,并为每个环境定义了 worker 的配置。默认情况下,这个配置选项包含了 production
和 local
环境。当然,你也可以根据需要随意添加更多环境:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
当你启动 Horizon 时,它将根据当前环境的 worker 进程配置选项对应用程序进行配置。通常情况下,环境由 APP_ENV
环境变量 的值确定。例如,默认的 Horizon local
环境配置为启动 3 个 worker 进程,并自动平衡分配给每个队列的 worker 进程的数量。而默认的 production
环境配置为最多启动 10 个工作进程,并自动平衡分配给每个队列的工作进程的数量。
注意:你需要确保
horizon
配置文件的 environment 部分包含你了你计划在 Horizon 上运行的每个环境选项。
Supervisors
正如您在 Horizon 的默认配置文件中所见。 每个环境可以包含一个或多个 「监督者」。 默认情况下,配置文件将此监督者定义为 supervisor-1
;但是,您可以随意命名您的监督者。 每个监督者本质上负责「监督」一组工作进程,并负责跨队列平衡工作进程。
如果您想定义一组应在该环境中运行的新工作进程组,您可以向给定环境添加额外的监督者。 如果您想为应用程序使用的给定队列定义不同的平衡策略或工作进程计数,您可以选择这样做。
默认值
在 Horizon 的默认配置文件中,您会注意到一个 defaults
配置选项。 此配置选项指定应用程序的 supervisors 的默认值。 监督者的默认配置值将合并到每个环境的监督者配置中,让您在定义监督者时避免不必要的重复。
平衡策略
与 Laravel 的默认队列系统不同,Horizon 允许您从三种工作线程平衡策略中进行选择:simple
、auto
和 false
。 simple
策略是配置文件的默认值,它在工作进程之间平均分配传入的任务:
'balance' => 'simple',
auto
策略根据队列的当前工作负载调整每个队列的工作进程数。 例如,如果你的 notifications
队列有 1000 个待处理的任务,而你的 render
队列是空的,Horizon 会分配更多的 worker 到你的 notifications
队列,直到队列为空。
当使用 auto
策略时,你可以定义 minProcesses
和 maxProcesses
选项来控制 Horizon 应该向上和向下扩展的最小和最大工作进程数:
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'tries' => 3,
],
],
],
balanceMaxShift
和 balanceCooldown
用于确定 Horizon 扩展速度以满足工作进程需求。 在上面的例子中,每三秒最多会创建或销毁一个新进程。 您可以根据应用的需要随意调整这些值。
当 balance
选项设置为 false
时,将使用默认的 Laravel 行为,它按照队列在你的配置中列出的顺序处理队列。
控制面板授权
Horizon 在 /horizon
URI 处公开了一个仪表板。 默认情况下,您只能在 local
环境中访问此仪表板。 但是,在您的 app/Providers/HorizonServiceProvider.php
文件中,有一个 授权 gate 定义。 此授权 gate 控制在 非本地 环境中对 Horizon 的访问。 您可以按需修改此 gate 以限制对 Horizon 的访问:
/**
* 注册 Horizon gate.
*
* 此方法决定了谁可以在非本地环境中访问 Horizon
*
* @return void
*/
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}
替代身份验证策略
请记住,Laravel 会自动将通过身份验证的用户注入到 gate 闭包中。 如果您的应用程序通过其他方法(例如 IP 限制)提供 Horizon 安全性,则您的 Horizon 用户可能不需要「登录」。 因此,您需要将上面的 function ($user)
闭包签名更改为 function ($user = null)
以强制 Laravel 不需要身份验证。
升级 Horizon
当 Horizon 升级到新的主版本时,请务必仔细阅读 升级指南。此外,当升级到任何一个新版本时,都应重新发布 Horizon 的资源文件:
php artisan horizon:publish
为了使资源文件保持最新并避免在将来的更新中出现问题,您可以将 horizon:publish
命令添加到应用的 composer.json
文件中的 post-update-cmd
脚本中:
{
"scripts": {
"post-update-cmd": [
"@php artisan horizon:publish --ansi"
]
}
}
运行 Horizon
一旦您在应用的 config/horizon.php
文件中配置了监督者和 workers,您就可以使用 horizon
Artisan 命令启动 Horizon。 这一个命令将为当前环境启动所有已配置的工作进程:
php artisan horizon
您可以使用 horizon:pause
Artisan 命令暂停 Horizon 进程,使用 horizon:continue
Artisan 命令指示它继续处理任务:
php artisan horizon:pause
php artisan horizon:continue
你也可以使用 horizon:pause-supervisor
和 horizon:continue-supervisor
Artisan 命令 暂停和继续特定 Horizon supervisors ::
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
技巧:如果你觉得自己配置 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
注意:要确保
stopwaitsecs
的值大于运行时间最长的任务所消耗的秒数。否则,Supervisor 可能会在工作完成前终止任务。
启动 Supervisor
创建了配置文件后,可以使用以下命令更新 Supervisor 配置并启动进程:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
技巧:关于 Supervisor 的更多信息,可以查阅 Supervisor 文档.
标记
Horizon 允许您将 tags
分配给任务,包括邮件、事件广播、通知和排队的事件监听器。实际上,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;
/**
* video 实例
*
* @var \App\Models\Video
*/
public $video;
/**
* 创建一个新的任务实例
*
* @param \App\Models\Video $video
* @return void
*/
public function __construct(Video $video)
{
$this->video = $video;
}
/**
* 执行任务
*
* @return void
*/
public function handle()
{
//
}
}
如果此任务与 App\Models\Video
实例一起排队,且该实例的 id
为 1
,则该作业将自动接收 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
*/
public function tags()
{
return ['render', 'video:'.$this->video->id];
}
}
通知
注意:当配置 Horizon 发送 Slack 或 SMS 通知时,您应该查看 相关通知渠道的先决条件。
如果您希望在其中一个队列等待时间较长时收到通知,您可以使用 Horizon::routeMailNotificationsTo
、Horizon::routeSlackNotificationsTo
和 Horizon::routeSmsNotificationsTo
方法。 您可以从应用程序 App\Providers\HorizonServiceProvider
的 boot
方法中调用这些方法:
/**
* 引导应用程序服务。
*
* @return void
*/
public function boot()
{
parent::boot();
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('example@example.com');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
配置通知等待时间阈值
您可以在应用程序的 config/horizon.php
配置文件中配置多少秒算作「长时间等待」。 此文件中的 waits
配置选项允许您控制每个连接/队列组合的长等待阈值:
'waits' => [
'redis:default' => 60,
'redis:critical,high' => 90,
],
指标
Horizon 包括一个指标仪表板,它提供了任务和队列的等待时间和吞吐量等信息。要让这些信息显示在这个控制面板上,您应该将 Horizon 的 snapshot
Artisan 命令配置为通过应用程序的 调度器 每五分钟运行一次:
/**
* 定义应用程序的命令调度
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('horizon:snapshot')->everyFiveMinutes();
}
删除失败的任务
如果你想删除一个失败的任务,你可以使用 horizon:forget
命令。 horizon:forget
命令接受失败任务的 ID 作为其唯一参数:
php artisan horizon:forget 5
从队列中清除任务
如果您想从应用程序的默认队列中删除所有任务,您可以使用 horizon:clear
Artisan 命令来执行此操作:
php artisan horizon:clear
您可以提供 queue
选项来从特定队列中删除任务:
php artisan horizon:clear --queue=emails