队列系统解决方案:Laravel Horizon
简介
Horizon 为 Laravel 提供了基于 Redis 的、拥有美观后台的、代码驱动配置的队列系统。Horizon 让我们可以轻松监控队列系统的关键指标,例如任务吞吐量、运行时间和失败任务等。
所有的队列进程配置都存放在一个单独的简单配置文件中,这样的话配置文件就可以存放到源码控制以便团队所有成员的协作。
安装
注:需要确保在
queue
配置文件中将队列驱动被设置为redis
。
我们使用 Composer 安装 Horizon 到 Laravel 项目:
composer require laravel/horizon
安装完成后,使用 Artisan 命令 horizon:install
发布资源:
php artisan horizon:install
还可以创建 failed_jobs
表用来存储执行失败的队列任务:
php artisan queue:failed-table
php artisan migrate
升级 Horizon
升级到最新版本的 Horizon 时,需要仔细阅读升级指南。
此外,你还要重新发布 Horizon 资源文件:
php artisan horizon:assets
配置
发布好前端资源后,主配置文件就会出现在 config/horizon.php
。在这个配置文件中,你可以配置队列进程选项以及每个包含目的描述的配置项,所以使用 Horizon 前务必浏览下这个配置文件。
注:需要确保
horizon
配置文件的environments
部分包含了每个你计划运行 Horizon 的环境的入口。
balance 配置项
Horizon 提供了三种负载均衡策略以供选择:simple
、auto
和 false
,simple
是默认策略,在进程之间平均分配进入任务:
'balance' => 'simple',
auto
策略基于队列当前负载调整每个队列的工作进程数量。例如,如果 notifications
队列有 1000 个等待执行的任务而 render
队列是空的,那么 Horizon 将会为 notifications
队列分配更多的工作进程直到队列为空。如果把 balance
选项设置为 false
,就会使用默认的 Laravel 行为,也就是按照配置文件中的排列顺序处理队列。
使用 auto
策略时,可以定义 minProcesses
和 maxProcesses
配置项来控制 Horizon 进程的最小值和最大值:
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'minProcesses' => 1,
'maxProcesses' => 10,
'tries' => 3,
],
],
],
任务裁剪
horizon
配置文件允许你配置最近和执行失败任务保留的时长(单位:分钟),默认情况下,最近任务保留1个小时,执行失败任务保留1周:
'trim' => [
'recent' => 60,
'failed' => 10080,
],
后台授权
我们可以通过 /horizon
访问 Horizon 后台:
默认情况下,你只能在 local
环境下访问这个后台。在 app/Providers/HorizonServiceProvider.php
文件中,有一个 gate
方法,该授权 gate 用于控制非本地环境对 Horizon 的访问。你可以按照需要编辑这个 gate
方法来限制用户对 Horizon 的访问:
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}
注:记住 Laravel 会自动注入认证用户到 Gate,如果你的应用通过其它方法来保障 Horizon 安全,例如 IP 限制,则你的 Horizon 用户不需要「登录」。因此,你需要修改上述
function ($user)
为function ($user = null)
来强制 Laravel 不要求认证。
运行 Horizon
如果你已经在配置文件 config/horizon.php
中配置过工作进程,就可以使用 Artisan 命令 horizon
来启动 Horizon,该命令会启动所有配置的工作进程:
php artisan horizon
你可以使用 Artisan 命令 horizon:pause
和 horizon:continue
来暂停或继续处理队列任务:
php artisan horizon:pause
php artisan horizon:continue
你还可以使用 Artisan 命令 horizon:terminate
来优雅地终止 Horizon 主进程 —— Horizon 会在所有当前正在执行的任务全部完成后退出:
php artisan horizon:terminate
部署 Horizon
如果要将 Horizon 部署到线上服务器,需要配置一个进程监控来监控 php artisan horizon
命令的运行并在异常退出的情况下重启该进程。部署新代码到服务器的时候,需要终止 Horizon 主进程以便通过配置的进程监控以最新代码重启进程。如上所述,我们可以通过 Artisan 命令 horizon:terminate
优雅地终止 Horizon 主进。
Supervisor 配置
如果你在使用 Supervisor 进程监控来管理 horizon
进程,可以像这样配置(按照自己的实际情况修改相应的目录信息):
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/app.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/app.com/horizon.log
注:如果你对如何管理自己的服务器感到摸不着头脑,可以考虑使用 Laravel Forge,Forge 提供了 PHP 7+ 版本的服务器,并且拥有运行最新版 Laravel 应用所需的所有软件工具集。
标签
Horizon 允许分配「标签」到任务,包括邮件、事件广播、通知以及队列事件监听器等。实际上,Horizon 会基于附加到任务的 Eloquent 模型为大部分任务以智能的方式自动打上标签。例如,我们来看看下面这个例子:
<?php
namespace App\Jobs;
use App\Video;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class RenderVideo implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The video instance.
*
* @var \App\Video
*/
public $video;
/**
* Create a new job instance.
*
* @param \App\Video $video
* @return void
*/
public function __construct(Video $video)
{
$this->video = $video;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}
如果任务被推送到队列时附带了一个 id
为 1
的 App\Video
实例,它将会自动打上 App\Video:1
的标签,这是因为 Horizon 会检查队列任务属性中的 Eloquent 模型,如果 Eloquent 模型被找到,Horizon 就会使用模型类名和主键为任务智能地打上标签:
$video = App\Video::find(1);
App\Jobs\RenderVideo::dispatch($video);
手动打标签
如果你想要手动定义某个队列对象的标签,可以在该类中定义一个 tags
方法:
class RenderVideo implements ShouldQueue
{
/**
* Get the tags that should be assigned to the job.
*
* @return array
*/
public function tags()
{
return ['render', 'video:'.$this->video->id];
}
}
通知
注:在使用通知之前,需要通过 Composer 安装
guzzlehttp/guzzle
依赖。在配置 Horizon 发送短信通知前,还要回顾下 Nexmo 通知驱动的预备知识。
如果你想要在某个队列任务等待很长时间后被通知,可以使用 Horizon::routeMailNotificationsTo
、Horizon::routeSlackNotificationsTo
以及 Horizon::routeSmsNotificationsTo
方法。你可以在 HorizonServiceProvider
中调用这些方法:
Horizon::routeMailNotificationsTo('example@example.com');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
Horizon::routeSmsNotificationsTo('15556667777');
配置通知等待时间下限
你可以在配置文件 config/horizon.php
中通过修改 waits
配置项来配置每个连接/队列上的等待时间下限(秒):
'waits' => [
'redis:default' => 60,
],
监控
Horizon 提供了一个监控后台查看任务和队列的等待时间和吞吐量信息,为了获取实时信息,可以配置 Horizon 的 Artisan 命令 snapshot
通过应用的调度器每五分钟运行一次:
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('horizon:snapshot')->everyFiveMinutes();
}
No Comments