跳到主要内容

RabbitMQ 3.12 性能改进

·13 分钟阅读

RabbitMQ 3.12 即将发布,其中包含许多新功能和改进。这篇博文重点介绍与性能相关的差异。最重要的变化是经典队列的 lazy 模式现在是标准行为(下面会详细介绍)。新的实现应该更节省内存,同时比早期版本的 lazynon-lazy 实现提供更高的吞吐量和更低的延迟。

为了获得更好的性能,我们强烈建议切换到经典队列版本 2 (CQv2)。

概述

让我们快速浏览一下 RabbitMQ 3.12 中最重要的与性能相关的改进。

经典队列:惰性模式的更改

从 3.12 开始,x-queue-mode=lazy 参数将被忽略。所有经典队列现在的行为都类似于之前的惰性队列。也就是说,消息倾向于写入磁盘,只有一小部分保留在内存中。内存中消息的数量取决于消费速率。此更改会影响所有经典队列用户。根据我们的测试,对于绝大多数用户来说,新的实现应该会带来显着的性能提升,并降低内存使用率。请继续阅读一些基准测试结果,但也要确保在升级前使用 PerfTest 测试您的系统。

经典队列:大幅改进的经典队列 v2 (CQv2)

注意

本段已更新以反映路线图的更改。经典队列版本 2 将成为 RabbitMQ 4.0 中的默认且唯一的选项(之前我们计划在 3.13 中将其设为默认选项)。

自 RabbitMQ 3.10 以来,我们有了经典队列的两种实现:原始版本 (CQv1) 和新版本 (CQv2)。它们之间的区别主要在于磁盘存储。

大多数用户仍然使用 CQv1,但从 3.12 开始,我们强烈建议切换到 CQv2,或至少评估一下。版本 2 将成为 RabbitMQ 4.0 中唯一可用的实现。

迁移过程很简单:在声明队列时,添加新的策略键或可选的队列参数 x-queue-version=2。要全局切换到 CQv2,请在配置文件中将 classic_queue.default_version 设置为 2

classic_queue.default_version = 2

可以通过将版本设置回 1 来返回。每次值更改时,RabbitMQ 都会转换队列的磁盘表示形式。对于大多数空队列,该过程是瞬间完成的。对于合理的积压,可能需要几秒钟。

在许多用例中,切换到 CQv2 将带来 20-40% 的吞吐量提升,同时降低内存使用率。那些仍然使用经典队列镜像的用户(你不应该这样做,它很快将被删除!),需要更彻底地测试你的系统,因为在某些情况下,版本 1 在镜像经典队列中效果更好,但也存在许多版本 2 更好,尽管没有任何镜像特定代码优化的场景。

新的 MQTT 实现:每个连接显着节省内存,支持每个节点数百万个连接

MQTT 插件已完全重新设计,可提供更低的内存使用率、更低的延迟,并且可以处理比以前更多的连接。

我们已经发布了一篇关于 MQTT 相关改进的单独博文

仲裁队列的重大改进

使仲裁队列更高效的工作仍在继续。RabbitMQ 3.12 带来了一些改进,因此所有仲裁队列用户都应该看到更好的性能。最大的变化将体现在具有长仲裁队列的环境中。以前,随着队列变长,其吞吐量会降低。这应该不再是一个问题。

节点应该更快地停止和启动

具有许多经典队列(数万个或更多)的 RabbitMQ 节点应该更快地停止和启动。这意味着在升级和其他维护操作期间,节点不可用时间更短。

基准测试设置

以下所有数据都比较了 3.11.7 和 3.12-rc.2。一些(主要是较小的)优化已反向移植到更新的 3.11 补丁版本中,这就是为什么此比较未使用最新的 3.11 补丁版本。

基准测试

在撰写本文时,RabbitMQ 团队的标准性能测试套件包含 14 个测试。每个测试运行 5 分钟。单独的环境同时运行相同的测试,但消息大小不同,以便可以轻松查看消息大小的影响,和/或比较相同工作负载下不同队列类型或版本。

以下是测试列表,按它们在 Grafana 仪表板中出现的顺序排列

  1. 一个发布者尽可能快地发布,而一个消费者尽可能快地消费
  2. 两个发布者,没有消费者(队列变长时的性能)
  3. 一个消费者消费来自上一个测试的长队列(消费者的性能不受发布者的影响)
  4. 五个队列,每个队列有 1 个发布者以 10k 消息/秒的速度发布,以及 1 个消费者(总预期吞吐量为 50k/秒,我们关注延迟)
  5. 扇出到 10 个队列 - 1 个发布者和 10 个消费者,一个扇出交换机
  6. 一个发布者,一个消费者,但只有 1 个未确认的消息(发布者在发送下一条消息之前等待上一条消息的确认)
  7. 扇入:7000 个发布者每秒发布 1 条消息,到一个队列
  8. 1000 个发布者发布 10 条消息/秒,每个发布到不同的队列;每个队列也有一个消费者(总预期吞吐量:10k/秒)
  9. 一个没有消费者的发布者创建消息积压,然后 10 个消费者加入以耗尽队列
  10. 与上一个类似,但 50 个消费者加入,但设置了 single-active-consumer(因此只有一个开始耗尽队列)
  11. 与第一个测试类似,但在消费者端使用了 1000 的多重确认
  12. 具有 TTL 的消息被发布并快速过期(它们永远不会被消费)
  13. 消息被发布以便稍后被否定确认(这只是下一个测试的设置)
  14. 来自上一个测试的消息被否定确认

最后几个测试不太有趣。引入它们是为了查找某些特定领域的问题。

环境

这些测试是使用以下环境进行的

apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
name: ...
spec:
replicas: 1 # or 3 for mirrored and quorum queues
image: rabbitmq:3.11.7-management # or rabbitmq:3.12.0-rc.2-management
resources:
requests:
cpu: 14
memory: 12Gi
limits:
cpu: 14
memory: 12Gi
persistence:
storageClassName: premium-rwo
storage: "150Gi"
rabbitmq:
advancedConfig: |
[
{rabbit, [
{credit_flow_default_credit,{0,0}}
]}
].

关于环境的一些注意事项

  1. 对于许多测试(甚至生产工作负载),这些资源设置都过高。这只是我们碰巧用于性能测试的配置,而不是建议
  2. 您应该能够使用更好的硬件达到更高的值。e2-standard-16 远非可以购买/租用的最佳硬件
  3. 信用流被禁用,因为否则单个快速发布者将被节流(以防止过载并为其他发布者提供公平的机会);流量控制在具有许多用户/连接的系统中很重要,但在基准测试中通常不是我们想要的

测试结果

经典队列和经典队列 v2 的新的类似惰性的默认行为

RabbitMQ 的初始版本于 2007 年发布。那时,与任何其他操作相比,磁盘访问速度非常慢。但是,随着存储技术的多年发展,越来越不需要避免将数据写入磁盘。在 3.6.0 版本中,早在 2015 年,就添加了惰性队列作为一种选择。惰性队列将所有消息存储在磁盘上以节省内存,这对于可能变长的队列尤其重要。如今,将消息写入磁盘是一项非常廉价的操作(除非您执行 fsync 以保证高数据安全性,就像仲裁队列所做的那样)。通过将消息存储在磁盘上,我们可以使用更少的内存。这意味着更低的成本、更少的内存警报以及更少由集群中突然的内存峰值引起的麻烦。因此,我们已将此作为经典队列的唯一选择。

3.11 非惰性 vs 3.12

让我们首先看看当前使用非惰性经典队列版本 1 (CQv1) 的用户在升级后期望发生什么。屏幕截图取自 12 字节消息大小测试。

Classic queues: non-lazy classic queues in 3.11 vs 3.12
经典队列:3.11 与 3.12 中的非惰性经典队列

如您所见,3.12 在每个测试中都表现更好:更高的吞吐量、更低的延迟、更小的可变性(更少的速率峰值)。与此同时,3.12 具有更低的内存使用率(类似于惰性队列)。在最后一个面板中,您可以看到 3.11 内存在使用队列变长时会出现峰值,而 3.12 仅在涉及许多连接的测试中超过 1GB 内存使用率(是连接使用了内存,而不是队列)。

3.12 CQv1 vs CQv2

上面,我们看到大多数用户在升级到 RabbitMQ 3.12 后应该获得的一些好处,但这仅仅是个开始!让我们将经典队列版本 2 添加到比较中

Classic queues: non-lazy classic queues in 3.11 vs 3.12 v1 and v2
经典队列:3.11 与 3.12 v1 和 v2 中的非惰性经典队列

使用 CQv2,我们观察到更高的吞吐量和更低的延迟。尤其是在队列变长的第二个测试中,版本 2 的延迟不超过 50 毫秒,而 3.11 可能会飙升到几秒钟。

请不要犹豫,尝试一下经典队列版本 2。您只需设置 x-queue-version=2 策略键即可。要全局切换到 CQv2,请在配置文件中将 classic_queue.default_version 设置为 2

classic_queue.default_version = 2

从 RabbitMQ 3.13 开始,版本 2 将成为默认版本。

经典镜像队列

如上所述,经典队列版本 1 包含一些专门实现的优化,以改善队列被镜像时的行为。当我们准备删除镜像功能时,版本 2 不再执行任何特殊的镜像技巧。因此,结果是好坏参半的,但版本 2 非常高效,即使没有特殊考虑,它在大多数情况下也能胜过版本 1,即使使用镜像也是如此。

Mirrored queues: 3.11 lazy and non-lazy vs 3.12 v2; 1kb messages
镜像队列:3.11 惰性和非惰性 vs 3.12 v2;1kb 消息

您可以看到版本 2 是否更适合您,但更重要的是,请开始迁移到仲裁队列,越快越好。

仲裁队列

仲裁队列多年来提供了比队列镜像更好的性能和数据安全性,并且它们只会变得更好。

3.12 中最大的改进是仲裁队列如何处理长积压。

Quorum queues: 3.11 vs 3.12; 5kb messages
仲裁队列:3.11 vs 3.12;5kb 消息

发布到长仲裁队列

在 RabbitMQ 3.12 之前,如果仲裁队列有很长的积压,发布延迟可能会显着增加,从而降低吞吐量。从 3.12 开始,队列的长度应该不再对延迟和吞吐量产生太大影响。

您可以在第二个测试中看到这一点:虽然两个版本都以大约 25k 消息/秒的速度开始,但 3.11 很快下降到 10k/秒左右。同时,3.12 保持在 20k/秒以上。

3.12 的性能不像我们希望的那样平稳,但已经好得多,并且可以进一步改进。同样重要的是要记住,在这些测试中,我们实际上是在过载队列:消息尽可能快地持续流入,因此任何垃圾回收或定期操作(例如 Raft 预写日志滚动)都将表现为延迟峰值。

消费吞吐量

从仲裁队列消费消息也比以前快得多。特别是,在我们的测试中,从非常长的队列消费消息的速度最多可以提高 10 倍。队列仍然应该是相对较短的(如果您需要存储大量消息,可以使用),但通过这些改进,仲裁队列应该能够很好地应对各种消息积压。

看一下最后一个面板(每秒消费消息数)上的第三个测试。3.12 以超过 15k 消息/秒的速度开始,并且随着队列变短而变得更快。同时,3.11 几乎无法超过每秒 1000 条消息。在此测试中,我们正在消费一个积压了 500 万条消息的队列,因此您可能从未见过仲裁队列如此挣扎,但好消息是:即使在这些情况下,仲裁队列现在也应该表现得更好且更可预测。

更可能的情况是消费者在一段时间内不可用并且需要赶上的情况。让我们关注这些测试

Quorum queues: 3.11 vs 3.12
仲裁队列:3.11 vs 3.12

在每个测试的第一个阶段,消费者关闭,并创建消息积压。然后消费者启动。在第一个测试中,有 10 个消费者,在第二个测试中,有 50 个消费者,但只有一个是活动的(作为单活动消费者)。在这两种情况下,3.12 都提供了显着更高的消费速率,并且队列耗尽得更快。在 3.11 的情况下,我们可以看到随着队列积压变短,它逐渐变得更快。

更快的节点重启

这不应该影响大多数用户,但对于具有许多经典队列的用户(例如,具有许多订阅的 MQTT 用户)来说,应该是非常好的消息。在我们的测试中,我们启动了一个节点,导入了 100,000 个经典队列版本 2,然后重启了该节点。3.12 在 3 分钟内启动并运行,而 3.11 需要 15 分钟才能再次为客户端提供服务。3.11 在启动时遇到了内存警报,这使得启动过程特别缓慢。有一个客户端正在运行,只是为了查看它何时失去连接并可以再次建立连接。

Node restart with 100k classic queues v2: 3.11 vs 3.12
具有 10 万个经典队列 v2 的节点重启:3.11 vs 3.12

不再有许多空闲队列导致的周期性资源使用峰值

您可能已经在上面的屏幕截图中注意到,3.11 不仅重启时间更长,而且发布速率也呈峰值状,即使它是一个非常轻的工作负载(每秒仅 100 条消息)。这些峰值是由一个内部进程引起的,该进程检查队列的运行状况以防止发出陈旧的队列指标。在 3.12 之前,它会查询每个队列的状态以确定队列是否健康。但是,空闲的经典队列会休眠(它们的 Erlang 进程被停止,其内存被压缩),并且需要唤醒才能回复它们是健康的。从 3.12 开始,休眠的队列被认为是健康的,而无需唤醒它们,因此即使在具有许多队列的节点上,CPU 和内存使用率也应该更低且更稳定。

结论

RabbitMQ 3.12 应该可以提高几乎所有用户的性能,通常会显着提高。与往常一样,我们衷心建议您在升级之前测试候选版本和新版本。我们也一直有兴趣了解人们如何在 GitHub Discussions 和我们的 社区 Discord 服务器 中使用 RabbitMQ。

如果您可以分享有关您的工作负载的信息,最好是以 perf-test 命令的形式,这将有助于我们为您改进 RabbitMQ。

© . All rights reserved.