Laravel Horizon队列监控:Supervisor配置、进程管理与高可用部署的最佳实践

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,
    ],
],

这里我们定义了 productionlocal 两个环境。 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-datanginx
  • 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 动态队列配置

在某些情况下,可能需要根据环境或运行时条件动态配置队列。 可以在 AppServiceProviderboot 方法中修改 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 队列。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注