基于 Laravel Horizon 管理队列处理器进程和伸缩策略


上篇教程中,我们已经介绍了如何基于负载监控和伸缩队列处理器,伸缩可以细分为水平伸缩和垂直伸缩,水平伸缩指的是新增/移除处理器进程/服务器,垂直伸缩指的是新增/移除服务器上的资源。

随着应用的增长,设计高效的伸缩策略变得越来越复杂,要伸缩每小时处理成千上万任务的多个队列,需要使用多种工具对系统进行持续监控。

为了简化这个工作,Laravel 提供了 Horizon 扩展包,它有一个仪表盘用于显示实时指标,以及一个内置的自动伸缩器,支持不同的伸缩策略。

注意:Horizon 目前仅支持 Redis 队列驱动。

启动和监控 Horizon 进程

使用 Horizon 后,不需要开启多个处理器进程,只需要启动 Horizon 即可:

php artisan horizon

你仍然可以使用 Supervisor 来管理 Horizon 进程,创建一个 /etc/supervisor/conf.d/horizon.conf 配置文件:

并基于它来启动和管理 Horizon 进程即可。

开启一个 Horizon 进程即可,无需通过 numprocs 配置多个进程,它会根据系统负载管理队列处理器进程的数量。

Horizon 的基本配置

我们可以通过配置文件 config/horizon.php 来配置 Horizon:

该配置的含义是启动 10 个 queue:work 进程,来消费 deploymentsnotifications 队列的任务:

通过 Supervisor 确保 Horizon 进程一直运行,Horizon 又会确保 10 个队列处理器进程一直运行,如果其中一个进程挂了,Horizon 会自动启动它。

你可以通过修改上述配置值来配置队列处理器进程:

还可以配置多个 Horizon Supervisor:

这样的话,Horizon 会启动 7 个处理器进程消费 deployments 队列任务,每个进程超时时间 300s,3 个处理器进程消费 notifications 队列任务,每个进程超时时间 60s。

简单伸缩策略

Horizon 目前提供了两种伸缩策略支持,通过 balance 配置项配置,一种是 simple 策略:

在这种策略下,Horizon 会启动 5 个处理器进程消费 deployments 队列,5 个处理器进程消费 notifications 队列。使用 simple 策略,Horizon 会为所有队列平均分配处理器进程。

如果不能均分,会分配给优先级更高的队列更多处理器进程:

deployments 队列将分配 3 个处理器进程,notifications 队列 2 个。

自动伸缩策略

另外一种是 auto 伸缩策略:

这个功能最强大,会根据队列的繁忙程度自动分配处理器进程,与此同时,需要配置 min_processesmax_processes

在上述配置中,Horizon 至少为每个队列分配 1 个处理器进程,总共是 10 个。如果 deployments 队列非常繁忙,而 notifications 是空的,则分配 9 个处理器给 deployments,1 个给 notifications

Horizon 会根据每个队列的预期清空时间来决定要分配的处理器数量,因此,如果 deployments 中的任务需要花费更多时间处理,而 notifications 瞬间就可以完成。即使 notifications 队列有比 deployments 更多的任务,Horizon 仍然会为 deployments 队列分配更多处理器进程。

伸缩频率

默认情况下,Horizon 每 3s 添加或移除一个处理器进程。因此,要将进程池中的进程数量从 5 提升到 8,需要耗费 9s。

你可以通过调整 balanceMaxShiftbalanceCooldown 配置项来修改这个伸缩频率:

这样一来,Horizon 每秒会新增或移除 3 个进程。

Horizon v5.0 之后才支持该功能。

避免内存泄露

和普通的队列处理器进程一样,Horizon 也是个常驻内存的 PHP 进程,需要确保在 Horizon 及其子进程运行完成后释放其占用的内存资源。

我们可以像普通处理器进程那样,每隔一段时间终止 Horizon 进程(会自动重启):

运行 horizon:terminate 会发送信号给所有关联的队列处理器进程,在运行完当前任务后退出。所有队列处理器进程退出后,Horizon 进程才会退出。这样一来,就可以避免内存泄露了。


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: 队列处理器进程的弹性伸缩

>> 下一篇: 处理队列任务生命周期的所有失败