在Laravel项目里,队列常用于处理耗时异步任务,比如发送邮件、生成报表、处理文件上传等,Horizon则提供了队列的监控、配置和管理能力。当项目存在多个队列,且不同队列的任务优先级不同时,就需要通过配置队列权重来让高优先级的队列优先获取处理资源。

环境准备
首先确保项目已经安装了Laravel框架和Horizon扩展,若未安装Horizon,可通过Composer执行以下命令安装:
composer require laravel/horizon
安装完成后,发布Horizon的配置文件和资源文件:
php artisan horizon:install
Horizon队列权重配置原理
Horizon的队列权重配置基于进程池的概念,每个进程池可以绑定多个队列,并且通过设置weight参数来定义队列的权重。权重数值越大,该队列在进程池中被分配到的处理进程占比就越高,任务被处理的优先级也就越高。
比如一个进程池绑定了两个队列,队列A的权重是2,队列B的权重是1,那么在进程池的3个可用处理进程中,大概会有2个进程优先处理队列A的任务,1个进程处理队列B的任务。
具体配置步骤
1. 打开Horizon配置文件
Horizon的配置文件位于项目根目录的config/horizon.php,打开该文件可以看到默认的配置结构。
2. 定义进程池并配置队列权重
在配置文件的environments数组中,找到对应运行环境的配置项,比如local或者production,在内部添加或修改supervisors配置,定义进程池并设置队列权重。
以下是一个配置示例,定义了名为default的进程池,绑定high_priority、medium_priority、low_priority三个队列,分别设置权重为3、2、1:
<?php
return [
// 其他默认配置...
'environments' => [
'local' => [
'supervisors' => [
'default' => [
'connection' => 'redis', // 队列使用的连接,默认是redis
'queue' => ['high_priority', 'medium_priority', 'low_priority'], // 绑定的队列列表
'weight' => [3, 2, 1], // 对应队列的权重,顺序和queue数组一致
'processes' => 6, // 该进程池的总进程数
'tries' => 3, // 任务失败重试次数
'timeout' => 60, // 任务超时时间,单位秒
],
],
],
'production' => [
'supervisors' => [
'default' => [
'connection' => 'redis',
'queue' => ['high_priority', 'medium_priority', 'low_priority'],
'weight' => [3, 2, 1],
'processes' => 12,
'tries' => 3,
'timeout' => 60,
],
],
],
],
];
注意queue数组和weight数组的顺序必须一一对应,第一个队列对应第一个权重值,第二个队列对应第二个权重值,以此类推。
3. 分发任务到对应队列
配置完成后,在分发队列任务时,需要指定任务要进入的队列名称,确保和配置中的队列名称一致。比如分发高优先级任务:
<?php
// 分发高优先级任务到high_priority队列
dispatch(new AppJobsSendOrderNotificationJob($order))->onQueue('high_priority');
// 分发低优先级任务到low_priority队列
dispatch(new AppJobsGenerateMonthlyReportJob())->onQueue('low_priority');
配置验证
配置完成后,先重启Horizon服务让配置生效:
php artisan horizon:terminate php artisan horizon
然后可以通过Horizon的仪表盘查看队列的运行状态,或者故意往不同队列投递大量任务,观察高权重队列的任务是否优先被处理。也可以在任务处理逻辑中添加日志,记录任务的开始处理时间,验证权重配置是否生效。
注意事项
- 权重配置是相对比例,不是绝对的执行顺序,只是提高高权重队列获取处理资源的概率,不是严格保证先处理高权重队列的所有任务再处理低权重队列任务。
- 如果多个进程池绑定了同一个队列,该队列的权重配置以对应进程池的配置为准,不同进程池之间的权重不会互相影响。
- 修改配置后必须重启Horizon服务,否则配置不会生效。
- 进程池的
processes参数决定了总的处理进程数,权重会基于这个总数来分配资源,总进程数过少可能导致权重效果不明显。