跳至主内容

RabbitMQ 3.12 性能改进

·阅读 13 分钟

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

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

概述

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

经典队列:惰性模式(Lazy Mode)的变更

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

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

注意

本段内容已更新以反映路线图的变更。经典队列 v2 将成为 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_version2

classic_queue.default_version = 2

通过将版本重新设置为 1 可以回退。每当值发生变化时,RabbitMQ 都会转换队列的磁盘表示形式。对于大部分为空的队列,该过程是瞬时的。对于有一定积压的队列,可能需要几秒钟。

在许多用例中,切换到 CQv2 将带来 20-40% 的吞吐量提升,同时降低内存占用。那些仍在使用经典队列镜像(不应再使用了,该功能很快会被移除!)的用户,需要更彻底地测试系统,因为在极少数情况下版本 1 在镜像经典队列上表现更好,但也有许多场景中,即使没有针对镜像的特定代码优化,版本 2 的表现也要好得多。

全新的 MQTT 实现:显著降低单连接内存占用,单节点支持百万级连接

MQTT 插件已完全重构,提供了更低的内存占用、更低的延迟,并且能够处理比以前多得多的连接。

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

仲裁队列(Quorum Queues)的显著改进

让仲裁队列更高效的工作仍在继续。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/s,我们关注延迟)
  5. Fanout(扇出)至 10 个队列 - 1 个发布者和 10 个消费者,使用 Fanout 交换机
  6. 一个发布者,一个消费者,但仅有 1 条未确认的消息(发布者在发送下一条之前等待上一条的确认)
  7. Fan-in(扇入):7000 个发布者,每个发布者以每秒 1 条消息的速度发布到单个队列
  8. 1000 个发布者以 10 条消息/秒的速度各自发布到不同的队列;每个队列也有一个消费者(预期的总吞吐量:10k/s)
  9. 一个没有消费者的发布者产生消息积压,然后 10 个消费者加入以清空队列
  10. 与上一个类似,但 50 个消费者加入,且设置了 Single-Active-Consumer(因此只有一个消费者开始清空队列)
  11. 与第一个测试类似,但在消费者端使用了 1000 条消息的 multi-ack
  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. 禁用了信用流控(Credit flow),因为否则单个快速发布者会被限速(为了防止过载并给其他发布者公平的机会);流控在有很多用户/连接的系统中很重要,但在进行基准测试时通常不是我们想要的。

测试结果

经典队列及其 v2 版本全新的类惰性默认行为

RabbitMQ 的最初版本发布于 2007 年。当时,与任何其他操作相比,磁盘访问速度非常慢。然而,随着存储技术的发展,避免将数据写入磁盘的必要性越来越小。在 2015 年的 3.6.0 版本中,添加了惰性队列作为一个选项。惰性队列将所有消息存储在磁盘上以节省内存,这对于可能变得很长的队列尤为重要。如今,将消息写入磁盘是一个相当低成本的操作(除非为了保证高数据安全性而执行 fsync,如仲裁队列所做的那样)。通过将消息存储在磁盘上,我们可以使用更少的内存。这意味着更低的成本、更少的内存警报以及由集群内存突然激增导致的更少头痛。因此,我们将其作为未来经典队列的唯一选项。

3.11 非惰性 vs 3.12

让我们看看当前使用非惰性经典队列 v1 (CQv1) 的用户在升级后预期的表现。截图取自 12 字节消息大小的测试。

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

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

3.12 CQv1 vs CQv2

上面我们看到了大多数用户在升级到 RabbitMQ 3.12 后应获得的一些好处,但我们才刚刚开始!让我们将经典队列 v2 加入对比。

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

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

请务必尝试经典队列 v2。您所需要做的就是设置 x-queue-version=2 策略键。要全局切换到 CQv2,请在配置文件中设置 classic_queue.default_version2

classic_queue.default_version = 2

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

经典镜像队列

如上所述,经典队列 v1 包含一些专门为改善队列镜像行为而实现的优化。随着我们准备移除镜像功能,版本 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 是否更适合您,但更重要的是,请尽快开始迁移到仲裁队列流 (Streams)

仲裁队列

仲裁队列多年来一直提供比队列镜像更好的性能和数据安全性,而且它们还在不断改进。

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/s 左右。与此同时,3.12 保持在 20k/s 以上。

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 个经典队列 v2,然后重启了该节点。3.12 在 3 分钟内启动并运行,而 3.11 需要 15 分钟才能再次开始为客户端服务。3.11 在启动时触发了内存警报,这使得引导过程特别缓慢。我们运行了一个客户端来观察它何时失去连接并能够重新建立连接。

Node restart with 100k classic queues v2: 3.11 vs 3.12
100k 经典队列 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。

© . This site is unofficial and not affiliated with VMware.