Laravel Horizon 队列监控:Supervisor 配置、进程管理与高可用部署的最佳实践
大家好,今天我们来深入探讨 Laravel Horizon 队列监控,以及如何通过 Supervisor 进行进程管理,最终实现高可用部署。Horizon 提供了美观的仪表盘和代码驱动的配置方式,极大地简化了队列的管理和监控。而 Supervisor 则可以确保 Horizon 进程始终运行,即便发生崩溃也能自动重启。
一、Laravel Horizon 简介与安装
Laravel Horizon 是一个为你的 Laravel Redis 队列提供的漂亮的仪表盘和代码驱动配置工具。它允许你轻松地监控队列的吞吐量、失败的任务以及进程运行时间。
1.1 安装 Horizon
首先,我们需要使用 Composer 安装 Horizon:
composer require laravel/horizon
1.2 发布 Horizon 资源文件
安装完成后,发布 Horizon 的配置文件和静态资源:
php artisan horizon:install
这将生成 config/horizon.php 配置文件,以及 /public/css/app.css 和 /public/js/app.js 等资源文件。
1.3 配置数据库连接
Horizon 使用数据库来存储任务的历史记录和统计信息。确保你的 config/database.php 文件中配置了正确的数据库连接。
1.4 配置 Redis 连接
Horizon 依赖 Redis 作为队列驱动。确保你的 .env 文件中配置了正确的 Redis 连接信息:
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
1.5 配置 Horizon
打开 config/horizon.php 文件,我们可以看到各种配置选项。 重要的配置项包括:
environments: 定义 Horizon 运行的环境。supervisor: 定义 Supervisor 的配置。path: Horizon 的路径。middleware: Horizon 访问的中间件。trim: Horizon 删除旧记录的配置。defaults: 默认的 worker 配置。environments: 不同环境下的队列配置。
例如,我们可以设置 environments 如下:
'environments' => [
'production' => [
'supervisor' => 'production',
'balance' => 'auto',
'maxProcesses' => 10,
'tries' => 3,
],
'local' => [
'supervisor' => 'local',
'balance' => 'simple',
'maxProcesses' => 3,
],
],
这里我们定义了 production 和 local 两个环境。 supervisor 是 Supervisor 的名称, balance 定义了负载均衡策略, maxProcesses 定义了最大进程数, tries 定义了任务失败后的重试次数。
二、Supervisor 配置
Supervisor 是一个进程管理系统,用于监控和控制 Linux 系统上的进程。 它可以确保 Horizon 进程始终运行,并在进程崩溃时自动重启。
2.1 安装 Supervisor
首先,需要在服务器上安装 Supervisor。 在 Debian/Ubuntu 系统上,可以使用以下命令安装:
sudo apt-get update
sudo apt-get install supervisor
在 CentOS/RHEL 系统上,可以使用以下命令安装:
sudo yum install epel-release
sudo yum install supervisor
2.2 创建 Horizon Supervisor 配置文件
我们需要为 Horizon 创建一个 Supervisor 配置文件。 该文件通常位于 /etc/supervisor/conf.d/ 目录下。
创建文件 horizon.conf:
sudo nano /etc/supervisor/conf.d/horizon.conf
将以下内容添加到文件中,并根据你的实际情况进行修改:
[program:horizon]
process_name=%(program_name)s
command=php /var/www/your-project/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/your-project/storage/logs/horizon.log
stopwaitsecs=3600
program:horizon: 定义 Supervisor 程序的名称。command: 定义启动 Horizon 的命令。 请确保路径/var/www/your-project/artisan指向你的 Laravel 项目的artisan文件。autostart: 定义是否在 Supervisor 启动时自动启动 Horizon。autorestart: 定义是否在 Horizon 崩溃时自动重启。user: 定义运行 Horizon 进程的用户。 通常是www-data或nginx。redirect_stderr: 定义是否将标准错误输出重定向到日志文件。stdout_logfile: 定义标准输出日志文件的路径。stopwaitsecs: 定义 Supervisor 停止进程时等待的时间。
2.3 更新 Supervisor 配置并启动 Horizon
保存 horizon.conf 文件后,需要更新 Supervisor 的配置并启动 Horizon:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
supervisorctl reread: 重新读取 Supervisor 的配置文件。supervisorctl update: 更新 Supervisor 的配置。supervisorctl start horizon: 启动 Horizon 进程。
你可以使用以下命令来检查 Horizon 的状态:
sudo supervisorctl status horizon
如果一切正常,你应该看到 horizon RUNNING。
三、进程管理与监控
配置好 Supervisor 后,可以使用 supervisorctl 命令来管理 Horizon 进程。
3.1 常用 Supervisor 命令
| 命令 | 描述 |
|---|---|
supervisorctl status |
查看所有进程的状态 |
supervisorctl start <program> |
启动指定的进程 |
supervisorctl stop <program> |
停止指定的进程 |
supervisorctl restart <program> |
重启指定的进程 |
supervisorctl reread |
重新读取配置文件 |
supervisorctl update |
更新配置 |
supervisorctl tail <program> |
查看指定进程的日志输出 |
supervisorctl tail -f <program> |
实时查看指定进程的日志输出 |
3.2 Horizon 仪表盘
启动 Horizon 后,可以通过浏览器访问 /horizon 路径来查看 Horizon 仪表盘。 在你的 .env 文件中配置 APP_DEBUG=true 可以在本地环境访问 Horizon。 在生产环境中,应该使用 APP_DEBUG=false 并配置适当的访问控制,例如使用中间件进行身份验证。
在仪表盘上,你可以查看队列的吞吐量、最近的任务、失败的任务以及进程运行时间等信息。 还可以手动重启 failed jobs。
四、高可用部署
为了实现 Horizon 的高可用部署,我们需要考虑以下几个方面:
4.1 多服务器部署
将 Horizon 部署在多台服务器上,可以提高系统的可用性和吞吐量。 每台服务器都运行 Horizon 和 Supervisor,并连接到同一个 Redis 队列。 Laravel 的队列系统会自动将任务分发到可用的 worker 进程。
4.2 负载均衡
使用负载均衡器(例如 Nginx 或 HAProxy)将流量分发到多台 Horizon 服务器。 这可以确保没有单点故障,并提高系统的性能。
4.3 数据库备份与恢复
定期备份 Horizon 使用的数据库,以防止数据丢失。 如果发生故障,可以使用备份来恢复数据库。
4.4 Redis 集群
使用 Redis 集群可以提高 Redis 的可用性和性能。 Redis 集群将数据分片存储在多个节点上,并在节点发生故障时自动进行故障转移。 Laravel 支持 Redis 集群,你需要在 config/database.php 文件中配置 Redis 集群连接信息。
4.5 监控与告警
设置监控系统来监控 Horizon 的运行状态,例如 CPU 使用率、内存使用率、队列长度等。 当出现异常情况时,及时发出告警。
五、代码示例与最佳实践
5.1 动态队列配置
在某些情况下,可能需要根据环境或运行时条件动态配置队列。 可以在 AppServiceProvider 的 boot 方法中修改 Horizon 的配置:
<?php
namespace AppProviders;
use IlluminateSupportServiceProvider;
use LaravelHorizonHorizon;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
if (app()->environment('production')) {
Horizon::auth(function ($request) {
// Authenticate users who can access Horizon...
// For example, check if the user is an administrator:
return auth()->check() && auth()->user()->isAdmin();
});
}
}
}
5.2 使用 Tags 标记任务
使用 Tags 可以方便地对任务进行分类和监控。 你可以在任务类中定义 tags 方法:
<?php
namespace AppJobs;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateQueueInteractsWithQueue;
use IlluminateQueueSerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Process the podcast...
}
/**
* Get the tags that should be assigned to the job.
*
* @return array
*/
public function tags()
{
return ['podcast', $this->podcast->id];
}
}
在 Horizon 仪表盘上,你可以根据 Tag 过滤任务。
5.3 自定义 Horizon 命令
你可以创建自定义 Horizon 命令来扩展 Horizon 的功能。 例如,可以创建一个命令来清理 Redis 队列。
首先,创建一个 Artisan 命令:
php artisan make:command ClearHorizonQueue
然后,修改生成的命令类:
<?php
namespace AppConsoleCommands;
use IlluminateConsoleCommand;
use IlluminateSupportFacadesRedis;
class ClearHorizonQueue extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'horizon:clear {queue?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear the specified Horizon queue';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$queue = $this->argument('queue') ?: config('queue.default');
$redis = Redis::connection('horizon');
$redis->del("queues:{$queue}");
$this->info("Queue {$queue} cleared successfully.");
return 0;
}
}
最后,在 app/Console/Kernel.php 文件中注册该命令:
<?php
namespace AppConsole;
use IlluminateConsoleSchedulingSchedule;
use IlluminateFoundationConsoleKernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
CommandsClearHorizonQueue::class,
];
/**
* Define the application's command schedule.
*
* @param IlluminateConsoleSchedulingSchedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
现在,你可以使用 php artisan horizon:clear 命令来清理指定的队列。
六、常见问题与排查
6.1 Horizon 无法启动
- 检查 Supervisor 配置文件是否正确,确保
command路径指向正确的artisan文件。 - 检查 Redis 连接是否正确,确保
.env文件中的 Redis 配置信息正确。 - 检查 Horizon 的日志文件,查看是否有错误信息。
- 确保运行 Horizon 的用户具有足够的权限。
6.2 队列任务执行失败
- 检查队列任务的代码,查看是否有异常抛出。
- 检查数据库连接是否正确,确保队列任务可以访问数据库。
- 检查 Redis 连接是否稳定,确保队列任务可以正常入队和出队。
- 查看 Horizon 仪表盘,查看是否有失败的任务。
6.3 Horizon 仪表盘无法访问
- 检查
.env文件中的APP_DEBUG是否设置为true。 - 检查 Horizon 的路由是否正确,确保可以访问
/horizon路径。 - 检查 Web 服务器的配置,确保可以正确代理到 Horizon 进程。
6.4 Supervisor 无法重启 Horizon 进程
- 检查 Supervisor 配置文件中的
autorestart是否设置为true。 - 检查 Horizon 进程是否正常退出,如果进程被强制杀死,Supervisor 可能无法自动重启。
- 检查 Supervisor 的日志文件,查看是否有错误信息。
七、总结
通过合理配置 Laravel Horizon 和 Supervisor,我们可以构建一个可靠且高效的队列处理系统。 掌握进程管理、高可用部署以及常见问题的排查技巧,能帮助我们更好地管理和维护 Laravel 应用。
Horizon、Supervisor 的良好配置是构建健壮队列系统的关键
通过本文的讲解,我们了解了 Laravel Horizon 的安装、配置、Supervisor 的配置、进程管理以及高可用部署的最佳实践。 希望这些知识能够帮助你更好地管理和监控 Laravel 队列。