跳到主要内容

集群大小调整案例研究 – 镜像队列 第 2 部分

·12 分钟阅读
Jack Vanlightly

上一篇文章中,我们开始了使用镜像队列对我们的工作负载进行大小调整分析。我们专注于消费者能够跟上的理想情况,这意味着没有队列积压,并且集群中的所有 broker 都正常运行。通过运行一系列基准测试,模拟不同强度的工作负载,我们确定了每 1000 msg/s 每月成本方面排名前 5 的集群大小和存储容量组合。

  1. 集群:5 节点,8 vCPU,gp2 SDD。成本:$58
  2. 集群:7 节点,8 vCPU,gp2 SDD。成本:$81
  3. 集群:5 节点,8 vCPU,st1 HDD。成本:$93
  4. 集群:5 节点,16 vCPU,gp2 SDD。成本:$98
  5. 集群:9 节点,8 vCPU,gp2 SDD。成本:$104

还需要进行更多测试,以确保这些集群能够处理 broker 故障以及在中断或系统速度减慢等情况下积累的大量积压。

恶劣条件 - 应对滚动重启和 broker 丢失

如果 broker 发生故障,我们基于 gp2 SSD 的集群是否能够处理相同的负载?可能是虚拟机或磁盘发生故障,或者您需要执行紧急操作系统补丁?我们真的需要确保在黑色星期五销售高峰期,即使面对故障,我们也能够处理流量。

为此,我们再次运行完全相同的测试,但在每个强度级别中途硬性终止一个 broker。

Fig 1. One broker killed during each test
图 1. 每次测试期间终止一个 broker

一些集群的表现优于其他集群,但没有集群在 broker 被终止时没有出现吞吐量下降的情况下完成测试。较小的 3 节点和 5 节点集群在较低强度测试中看到了这种下降,而 7 节点和 9 节点集群仅在较高强度下才开始看到这种下降。

让我们看看我们 30k msg/s 的目标速率周期。

Fig 2. Different clusters handle a lost broker better than others
图 2. 不同的集群处理 broker 丢失的能力优于其他集群

您会注意到 5x16、7x16、7x8 和 9x8 集群完全恢复,而在频谱的另一端,3 节点集群的下降幅度最大。对于完全恢复的集群,下降幅度很小,但这是在 *ha-sync-mode* 为 *manual* 的情况下。如果您选择 *automatic*,恢复仍然会发生,但下降幅度更大且持续时间更长。

吞吐量下降的原因是,当镜像队列因 broker 丢失而变得副本不足时,如果可以,它将在另一个 broker 上创建新镜像 - 保持相同的冗余级别。这会将相同数量的流量集中在更少的 broker 上。因此,如果您像本测试中那样使用 2 的复制因子(一个 master,一个 mirror),有三个 broker 并丢失一个,那么您将使其他两个 broker 上的负载增加相当大的百分比。如果您使用 *ha-mode=all*,那么您不会看到这样的下降,因为将没有 broker 可以放置新镜像。

但是,如果您有九个节点并丢失一个,那么负载增加是微不足道的。

横向扩展在此轮胜出。

恶劣条件 - 消费者速度减慢

在处理消息时,消费者通常需要与数据库或第三方 API 等其他系统进行交互。这些下游系统可能会因负载过重或某种中断而减速,这会产生连锁反应,导致您的消费者速度减慢。然后,这会导致您的队列中消息数量增加,这也会影响发布者。当队列较小或为空时(空是因为消息立即被消费),RabbitMQ 可提供最佳性能。

我们的要求规定,如果我们遇到消费者速度减慢的情况,发布应继续不受影响,即使在 30k msg/s 的目标峰值负载下也是如此。

在此测试中,每条消息的处理时间各不相同

  • 5 分钟,10 毫秒
  • 在 20 分钟内从 10 毫秒增长到 30 毫秒
  • 5 分钟,30 毫秒
  • 在 20 分钟内从 30 毫秒减少到 10 毫秒
  • 50 分钟,10 毫秒

消息积压可能会增长到数千万,因为这是一个高流量系统,积压可能会快速形成。我们将看到消费率呈 S 形,因为首先处理时间增加,然后减少,并且当消费者处理积压时,消费率将超过发布率。

随着消费率恢复,但队列长度仍然很大,这时我们可能会看到对发布者的影响。发布率可能会下降一段时间,直到积压被清除。性能更高的集群应该看不到影响或仅在短时间内受到影响。

我们将以三种不同的发布速率运行测试

  • 10k msg/s,在 100 个队列中分布 200 个消费者。最高消费率为 20k msg/s,然后在 30 毫秒处理时间时降至 6.6k msg/s。
  • 20k msg/s,在 100 个队列中分布 300 个消费者。最高消费率为 30k msg/s,然后在 30 毫秒处理时间时降至 10k msg/s。
  • 30k msg/s,在 100 个队列中分布 400 个消费者。最高消费率为 40k msg/s,然后在 30 毫秒处理时间时降至 13.3k msg/s。

Fig 3. Consumer slowdown test at publish rates 10k msg/s, 20k msg/s and 30k msg/s.
图 3. 在发布速率为 10k msg/s、20k msg/s 和 30k msg/s 下的消费者速度减慢测试。

查看队列积压变得有多大的一些示例。

3x16 集群

Fig 4. Queue backlog size for the 3x36 cluster
图 4. 3x36 集群的队列积压大小

7x16 集群

Fig 5. Queue backlog size for the 7x16 cluster
图 5. 7x16 集群的队列积压大小

队列积压增长相当大,但即便如此,我们也仅达到 11GB 最大内存限制的 50%。我们正在使用服务器内存 40% 的默认内存高水位线。

Fig 7. Memory usage and memory high watermark for the 7x16 cluster.
图 7. 7x16 集群的内存使用率和内存高水位线。

9x8 集群

Fig 6. Queue backlog size for the 9x8 cluster
图 6. 9x8 集群的队列积压大小

Fig 8. Memory usage and memory high watermark for the 9x8 cluster.
图 8. 9x8 集群的内存使用率和内存高水位线。

较小的 8 vCPU 实例的可用内存只有一半,高水位线为 6GB,但这些测试仍然只使用了大约一半的内存。

在 10k msg/s 时,所有集群都能处理消费者速度减慢和相关的积压。

在 20k msg/s 时,只有 7x16 和 9x8 集群能够在不降低发布速率的情况下处理它。7x8 集群非常接近能够处理它。其余集群的发布速率都出现下降,因为在队列积压仍然很高的情况下,消费者和发布者之间存在争用。长队列效率较低,这既是由于磁盘使用量增加,也是由于内存数据结构。请注意,这是我们预期的峰值负载,但为了以防万一(在 30k msg/s 时),我们希望将大小调整为高于该值。

但在 30k msg/s 时,我们的集群都无法在整个消费者速度减慢期间持续处理 30k msg/s。最好的是 7x16 和 9x8 集群,它们的发布速率降低了大约 20-25 分钟。

因此,我们要么认为这已经足够好,要么我们需要使用更大的集群,例如 9x16 或 11x8 集群。

Fig 9. The 30k msg/s test with 9x16 and 11x8 clusters.
图 9. 使用 9x16 和 11x8 集群进行的 30k msg/s 测试。

9x16 集群可以处理 30k msg/s 的负载,但发布速率略有波动。对于 8 vCPU 实例,看起来我们需要增加到 13 个或更多实例。这些都是大型集群,但这也是一个非常苛刻的工作负载。

您可以使用 PerfTest(从 2.12 及更高版本)运行类似的测试

bin/runjava com.rabbitmq.perf.PerfTest \
-H amqp://guest:guest@10.0.0.1:5672/%2f,amqp://guest:guest@10.0.0.2:5672/%2f,amqp://guest:guest@10.0.0.3:5672/%2f \
-z 1800 \
-f persistent \
-q 1000 \
-c 1000 \
-ct -1 \
--rate 100 \
--size 1024 \
--queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 \
--queue-pattern-to 100 \
--producers 200 \
--consumers 200 \
--producer-random-start-delay 30 \
-vl 10000:300 \
-vl 11000:60 -vl 12000:60 -vl 13000:60 -vl 14000:60 -vl 15000:60 -vl 16000:60 -vl 17000:60 -vl 18000:60 -vl 19000:60 \
-vl 20000:60 -vl 21000:60 -vl 22000:60 -vl 23000:60 -vl 24000:60 -vl 25000:60 -vl 26000:60 -vl 27000:60 -vl 28000:60 -vl 29000:60 \
-vl 30000:300 \
-vl 29000:60 -vl 28000:60 -vl 27000:60 -vl 26000:60 -vl 25000:60 -vl 24000:60 -vl 23000:60 -vl 22000:60 -vl 21000:60 -vl 20000:60 \
-vl 19000:60 -vl 18000:60 -vl 17000:60 -vl 16000:60 -vl 15000:60 -vl 14000:60 -vl 13000:60 -vl 12000:60 -vl 11000:60 -vl 10000:60 \
-vl 10000:3000

恶劣条件 - 发布速率峰值超过消费者容量

与消费者速度减慢类似,我们最终会遇到发布速率超过消费速率导致消息积压的情况。但这次是由发布速率的巨大峰值引起的,我们的后端系统无法处理该峰值。吸收发布速率的峰值是选择消息队列的原因之一。您无需扩展后端系统来处理峰值负载(这可能很昂贵),而是允许消息队列吸收额外的流量。然后,您可以在一段时间内处理积压。

在此测试中,我们将处理时间保持在 10 毫秒,但先增加发布速率,然后再降低

  • 5 分钟,基本速率
  • 在 20 分钟内从基本速率增长到峰值
  • 5 分钟,峰值。
  • 在 20 分钟内从峰值降低到基本速率
  • 50 分钟,基本速率

我们将运行三个测试

  • 10k msg/s 基本发布速率,20k msg/s 峰值。200 个消费者,最高消费率为 13k msg/s。
  • 20k msg/s 基本发布速率,30k msg/s 峰值。300 个消费者,最高消费率为 23k msg/s。
  • 30k msg/s 基本发布速率,40k msg/s 峰值。400 个消费者,最高消费率为 33k msg/s。

Fig 10. 10k msg/s base rate, 20k msg/s peak with up to 7k msg/s consumer rate deficit.
图 10. 10k msg/s 基本速率,20k msg/s 峰值,最多 7k msg/s 消费率赤字。

7x16、9x8、7x8 集群可以处理峰值,而 5x8 集群大部分时间可以处理,只是发布速率偶尔会下降。其他集群也很接近,但无法处理目标速率。

Fig 11. 20k msg/s base rate, 30k msg/s peak with up to 7k msg/s consumer rate deficit.
图 11. 20k msg/s 基本速率,30k msg/s 峰值,最多 7k msg/s 消费率赤字。

只有 7x16 和 9x8 集群可以处理它,但 5 节点集群也很接近。

Fig 12. 30k msg/s base rate, 40k msg/s peak with up to 7k msg/s consumer rate deficit.
图 12. 30k msg/s 基本速率,40k msg/s 峰值,最多 7k msg/s 消费率赤字。

只有 7x16 集群达到了 40k msg/s 的发布速率,但 9x8 集群也很接近。7x16 集群的message backlog 接近 700 万条消息,但它仍然能够处理。

您可以使用 PerfTest 运行类似的测试

bin/runjava com.rabbitmq.perf.PerfTest \
-H amqp://guest:guest@10.0.0.1:5672/%2f,amqp://guest:guest@10.0.0.2:5672/%2f,amqp://guest:guest@10.0.0.3:5672/%2f \
-z 1800 \
-f persistent \
-q 1000 \
-ct -1 \
-c 1000 \
--size 1024 \
--queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 \
--queue-pattern-to 100 \
--producers 200 \
--consumers 200 \
--producer-random-start-delay 30 \
--consumer-latency 10000 \
-vr 100:300 \
-vr 102:60 -vr 104:60 -vr 106:60 -vr 108:60 -vr 110:60 -vr 112:60 -vr 114:60 -vr 116:60 -vr 118:60 -vr 120:60 \
-vr 122:60 -vr 124:60 -vr 126:60 -vr 128:60 -vr 130:60 -vr 132:60 -vr 134:60 -vr 136:60 -vr 138:60 -vr 140:60 \
-vr 142:60 -vr 144:60 -vr 146:60 -vr 148:60 -vr 150:60 \
-vr 148:60 -vr 146:60 -vr 144:60 -vr 142:60 -vr 140:60 -vr 138:60 -vr 136:60 -vr 134:60 -vr 132:60 -vr 130:60 \
-vr 128:60 -vr 126:60 -vr 124:60 -vr 122:60 -vr 120:60 -vr 118:60 -vr 116:60 -vr 114:60 -vr 112:60 -vr 110:60 \
-vr 108:60 -vr 106:60 -vr 104:60 -vr 102:60 -vr 100:60 \
-vr 100:3000

恶劣条件测试结论

在执行理想情况测试后,我们有许多集群可以处理峰值负载,因此我们最终得到了每 1000 msgs/s 每月成本方面排名前 5 的集群排行榜。现在,在运行恶劣条件测试后,我们从原始集合中缩小到两个潜在的集群

  • 集群:7 节点,16 vCPU,gp2 SSD。成本:每 1000 msg/s $104
  • 集群:9 节点,8 vCPU,gp2 SDD。成本:每 1000 msg/s $81

在理想情况下,横向扩展较小的虚拟机为我们提供了最佳的最高吞吐量和成本效益。但在考虑到弹性测试时,7x16 是最好的全能集群。

当然,即使 7x16 集群也在 30k msg/s 消费者速度减慢测试中表现吃力。因此,我们可能仍然需要考虑以下集群

  • 集群:9 节点,16 vCPU,gp2 SSD。成本:每 1000 msg/s $133
  • 集群:11 节点,8 vCPU,gp2 SSD。成本:每 1000 msg/s $99

镜像队列案例研究要点

注意不要只测试简单的场景,例如我们的第一个理想情况测试,其中发布速率是恒定的,而消费速率是固定的 - 您只是在理想条件下调整 RabbitMQ 的大小。如果您需要 RabbitMQ 提供一定的吞吐量,即使在逆境中也是如此,那么您需要包含像我们在本文中运行的测试。在负载较重的情况下,您更有可能看到不利的情况。由慢速消费者引起的队列积压更可能在更广泛的系统处于重负载下时发生。同样,可能是流量峰值导致发布速率超过消费速率。因此,在峰值条件及更高条件下进行测试对于确保集群能够弹性应对您预期的负载非常重要。

最重要的是,RabbitMQ 可以很好地处理 broker 丢失,但它更难以应对的是队列积压。我们的顶级集群,7x16 和 9x8 配置在理想条件下达到 65-70k msg/s,但在我们施加的最恶劣条件下仅达到 20k msg/s。我说只有 20k msg/s,但这相当于每天 17 亿条消息,这高于 RabbitMQ 的大多数用例。

最后...这是一个特定的工作负载,请查看第一篇文章中的其他建议,这些建议可能适用于其他工作负载和场景。

© . All rights reserved.