集群规模和其他注意事项
这是我们关于RabbitMQ集群规模的一系列短文章的开始。实际的规模完全取决于您的硬件和工作负载,因此,我们不会告诉您应该配置多少个CPU和多少内存,而是会提供一些通用准则,并使用一个案例研究来展示您应该考虑哪些因素。
常见问题
对于您的RabbitMQ集群,VM大小和VM数量的最佳组合是什么?您应该纵向扩展并使用三个具有32个CPU线程的节点吗?还是应该横向扩展并使用9个具有8个CPU线程的节点?哪种类型的磁盘性价比最高?您需要多少内存?哪种硬件配置更适合吞吐量、延迟、拥有成本?
首先,没有唯一的答案。如果您在云中运行,那么选项较少,但如果您在本地运行,那么大量的虚拟化、存储和网络产品以及配置将使这个问题变得不可能回答。
虽然没有一个包含硬性数字的单一规模指南,但我们可以进行规模分析,希望这将有助于您进行自己的规模设计。
一些常见的注意事项
队列和客户端的数量
客户端连接和队列需要计算时间和内存。如果您有数千个队列和客户端,那么这将影响您的规模设计。数量越多,您可能需要的CPU核心和内存就越多。对于RabbitMQ来说,拥有少量队列和客户端更有效率 - 这样可以减少CPU上下文切换和内存开销。因此,如果您有一个包含数千个队列和客户端的工作负载,那么与只有少数几个队列和客户端相比,您将需要更大的VM或更多的VM才能获得相同的总吞吐量。
各种类型的波动会影响集群
- 连接波动(连接的打开和关闭)
- 队列和队列绑定波动(队列和绑定的创建和删除)
什么是高吞吐量?
每天一百万条消息可能听起来很多,但当您将其计算为每秒速率时,它仅降至略低于每秒12条消息。当您的吞吐量低于每秒100条时,您可能能够将VM的大小设计得很小。除非您有数千个队列和客户端,否则您不太可能遇到资源瓶颈。如果您使用集群,在这种速率下,它将纯粹是为了冗余而不是性能。
一旦您超过每秒1000条消息(每天8600万条或每月25亿条),这时您就需要花更多时间进行规模分析。对于每秒1000条消息,您仍然可以设计得非常小,但可能存在一些资源瓶颈。
超过10000(每天8.6亿条或每月250亿条),我建议您更加彻底地进行规模分析。我们说“快乐的Rabbit是空Rabbit”。当队列为空时,RabbitMQ提供最佳的吞吐量和最低的延迟。消息从内存中提供服务,通常根本不写入磁盘。当您有大量积压时,消息很可能读取/写入磁盘,数据结构更大且效率更低,并且不可避免地,集群可以处理的最大吞吐量会下降。当您有高吞吐量集群时,积压可能会从无到百万迅速增长,因此您需要确保您对此进行规模设计。不要只针对理想情况进行规模设计,也要针对不利情况进行规模设计。我们稍后将更详细地介绍这一点。
哪些工作负载需要更多内存?
经典队列和经典镜像队列会将消息保存在内存中,但在内存不足时开始逐出消息。很难预测,因为它是一种动态行为。一个好的做法是在您有非常大的队列时使用惰性队列,因为惰性队列会非常早地从内存中逐出消息,并且通常使用更少的内存。
默认情况下,仲裁队列始终将所有消息存储在内存中,即使在内存不足的情况下也是如此。这意味着,除非您更改默认设置,否则当队列变长时,您更有可能遇到内存警报(这会阻止所有发布者)。好消息是可以使用几个队列属性(例如 x-max-in-memory-length)进行配置,这将一次只将配置数量的消息保存在内存中。这可能应该在您所有的仲裁队列上设置。但这确实意味着更多消息将从磁盘读取,因此它不是免费的。
冗余如何影响规模设计?
如果您使用仲裁队列或镜像队列,则每条消息都将传递到多个代理。如果您有一个包含三个代理的集群和具有3个复制因子的仲裁队列,那么每个代理都将接收每条消息。在这种情况下,我们仅为冗余创建了一个集群。但我们也可以为可扩展性创建更大的集群。我们可以有一个包含9个代理的集群,以及具有3个复制因子的仲裁队列,现在我们已经分散了负载,并且可以处理更大的总吞吐量。
添加更多冗余将减少总吞吐量,因此,为了抵消这一点,您可以向集群中添加更多代理来分散负载。
消息大小如何影响规模设计?
小消息(例如小于1kb)不太可能使网络或磁盘饱和。如果您遇到资源瓶颈,它可能是CPU。但是,我们可以很容易地用更大的消息大小使网络和磁盘饱和。如果您有1Mb的消息和一个5gbit的网络,那么使用经典队列,您将在略高于300 msg/s时使网络饱和。如果您使用仲裁队列或镜像队列,它将更低,因为消息不仅必须接收/发送到客户端,而且必须在代理之间复制。
您计划使用联合吗?
联合涉及在RabbitMQ代理上运行AMQP客户端。这意味着它们与通道和队列争夺相同的资源。这可能意味着您需要增加VM的大小/数量。还要考虑到
- 如果您对本地队列(充当出站队列)使用镜像队列,那么您将在集群中复制这些传出消息。冗余是昂贵的。
- 消息将通过网络传输到另一个代理,这增加了您的网络规模设计。
始终将联合设置包含在您的规模测试中。
规模设计 - AWS 案例研究
我们现在将介绍一个案例研究,其中包括识别工作负载、定义我们关于端到端延迟和弹性的要求,以及一套将不同VM大小和数量与这些要求进行比较的测试。
请记住,这是一个案例研究,因此您自己的要求和工作负载可能与此大不相同。它也是一项详细的分析,适用于RabbitMQ 是您基础架构的关键部分并且值得投入时间进行彻底的规模分析的情况。
案例研究工作负载
为了我们的案例研究的目的,我选择了一个中等规模、高强度的工作负载。这意味着我们有适量的队列和客户端,但要推送大量消息。
- 200 个发布者
- 100 个队列
- 200 个消费者
- 无扇出 - 使用默认交换机进行点对点
- 1kb 消息大小
- 每条消息处理时间 10 毫秒
- Java 客户端
- 恒定速率
我们大部分时间都具有相对稳定的 5000 msg/s 的吞吐量,但每天可能会出现高达 20,000 msg/s 的峰值,持续时间可达一到两个小时。我们预计流量在未来一年内可能会增长 10%。
我们的要求
我们希望根据峰值吞吐量以及额外的 10,000 msg/s 进行集群规模设计,以防出现意外的高流量,这也能覆盖预期的 10% 增长。我们不需要根据我们预期的增长进行规模设计,因为我们将来可以轻松升级 EC2 实例。
我们希望使用复制队列,因为这些消息对企业具有货币价值。因此,我们将对镜像队列和较新的仲裁队列进行规模分析。
在延迟方面,只要我们在 99% 分位的端到端(从发布到消费消息之间的时间)低于 1 秒,我们都感到满意。
最后,我们希望确保即使在不利条件下(例如丢失代理或下游中断影响消费者吞吐量)我们也能达到我们的峰值吞吐量。如果由于消费者运行缓慢而导致队列积压,我们希望吸收消息入口(保持发布速率)。发布速度变慢对我们来说有货币成本。
测试
理想条件 - 提高强度测试
对于发布速率,我们将实际运行一系列使用不同发布速率的基准测试,涵盖 5k 到 30k 消息/秒及更高。我们的目标是确定所选规模在多大程度上能够提供必要的性能,以及何时开始出现性能下降并无法满足需求。这是理想情况,RabbitMQ 很可能保持为空(并且速度最快),因为消费者始终能够跟上(直到集群达到其容量)。
我们将使用以下速率(所有发布者的总和)
- 1000 消息/秒(每小时=360万,每天=8640万,每月=25亿)
- 5000 消息/秒(每小时=1800万,每天=4.32亿,每月=129亿)
- 10000 消息/秒(每小时=3600万,每天=8.64亿,每月=258亿)
- 15000 消息/秒(每小时=5400万,每天=13亿,每月=389亿)
- ...
- 70000 消息/秒(每小时=2.52亿,每天=60亿,每月=1814亿)
不利条件 - 丢失代理测试
我们将测试所选集群规模即使在某个代理宕机的情况下,是否能够处理峰值速率(30k 消息/秒)。代理可能由于多种原因宕机:我们在操作系统补丁安装过程中重新启动机器,磁盘发生故障,网络分区等。最糟糕的情况是这种情况发生在峰值负载期间。
不利条件 - 消费速率下降,造成积压测试
数据库服务器过载,或者下游系统网络速度变慢,或者第三方 API 运行缓慢。无论哪种情况,处理每条消息的时间都从 10 毫秒增加到 30 毫秒,导致消费速率下降。我们能否继续不受影响地接受发布速率并吸收积压?
不利条件 - 发布速率峰值非常高,超过消费者容量测试
营销活动变得病毒式传播,我们获得了比预期多得多的流量,以至于我们的处理系统无法承受负载。我们能否吸收大量排队积压的流量,以便稍后处理?
集群
集群规模和存储卷
我们正在 AWS ec2 上运行,云计算不就是巨大的 API 吗?我们可以轻松地自动创建任何集群规模或任何我们想要的 ec2 实例类型。
我们将在这 7 种不同的集群配置上运行所有这些测试,并使用三种不同的存储卷类型(io1、gp2、st1)。
集群
- 3 个节点,c5.9xlarge,36 个 vCPU,72 GB RAM = 108 个 vCPU
- 3 个节点,c5.4xlarge,16 个 vCPU,32GB RAM = 48 个 vCPU
- 5 个节点,c5.4xlarge,16 个 vCPU,32GB RAM = 80 个 vCPU
- 7 个节点,c5.4xlarge,16 个 vCPU,32GB RAM = 112 个 vCPU
- 5 个节点,c5.2xlarge,8 个 vCPU,16 GB RAM = 40 个 vCPU
- 7 个节点,c5.2xlarge,8 个 vCPU,16 GB RAM = 56 个 vCPU
- 9 个节点,c5.2xlarge,8 个 vCPU,16 GB RAM = 72 个 vCPU
c5.9xlarge 可以被认为是一个非常大的虚拟机,它绝对是你可能使用的最大的虚拟机。c5.4xlarge 可以被认为是大实例,而 c5.2xlarge 是中等大小的实例。这是一个非常密集的工作负载,这就是为什么我们在分析中包含这些大型实例类型的原因。许多不太密集的工作负载适用于较小的实例类型,例如具有 4 个 vCPU 的 c5.xlarge 或 c5.large。如果这些计算优化实例上的内存曾经成为问题,那么更大的内存实例类型(m5、r5)也是一个选项。
卷
- io1(预置 IOPS SSD),200GB,10000 IOPS,500MiB/s 最大值 = 每月 725 美元
- gp2(通用 SSD),1000GB,3000 IOPS,250 MiB/s 最大值 = 每月 100 美元
- st1(高吞吐量 HDD),7000GB,280 MB/s 基线,500MiB/s 最大值 = 每月 315 美元
io1 对于其大小而言具有大量的 IOPS,这使得它价格昂贵。这种选择在某种程度上是为了展示更昂贵的磁盘如何在规模分析中发挥作用。从案例研究中您将看到,我们可以使用 IOPS 较低的 io1,并且它会更具成本效益。
我们选择了一个大型 gp2,因为较小的卷会获得突发积分,这可能会让你感到意外。同样,我们选择了 7TB HDD 也是因为突发积分。此大小仍然具有突发性,但这些工作负载不会过多地使用突发性,并且在成本之间取得了平衡(此大小将管理 2 小时的峰值)。对于强度较低的工作负载,我们可以选择更小的尺寸并节省一些资金。
这是一个使用 1TB HDD 的测试,其中突发性已用完。
每月成本(按需定价)
io1 SSD
成本为:虚拟机 + 卷 = 总计
- 3 个节点,c5.9xlarge = 36 个 vCPU,每月成本 3300 美元 + 2175 美元 = 5475 美元
- 3 个节点,c5.4xlarge = 16 个 vCPU,每月成本 1468 美元 + 2175 美元 = 3643 美元
- 5 个节点,c5.4xlarge = 16 个 vCPU,每月成本 2445 美元 + 3625 美元 = 6070 美元
- 7 个节点,c5.4xlarge = 16 个 vCPU,每月成本 4123 美元 + 5075 美元 = 9198 美元
- 5 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1225 美元 + 3625 美元 = 4850 美元
- 7 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1715 美元 + 5075 美元 = 6790 美元
- 9 个节点,c5.2xlarge = 8 个 vCPU,每月成本 2205 美元 + 6525 美元 = 8730 美元
gp2 SSD
成本为:虚拟机 + 卷 = 总计
- 3 个节点,c5.9xlarge = 36 个 vCPU,每月成本 3300 美元 + 300 美元 = 3600 美元
- 3 个节点,c5.4xlarge = 16 个 vCPU,每月成本 1468 美元 + 300 美元 = 1768 美元
- 5 个节点,c5.4xlarge = 16 个 vCPU,每月成本 2445 美元 + 500 美元 = 2945 美元
- 7 个节点,c5.4xlarge = 16 个 vCPU,每月成本 4123 美元 + 700 美元 = 4823 美元
- 5 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1225 美元 + 500 美元 = 1725 美元
- 7 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1715 美元 + 700 美元 = 2415 美元
- 9 个节点,c5.2xlarge = 8 个 vCPU,每月成本 2205 美元 + 900 美元 = 3105 美元
st1 HDD
成本为:虚拟机 + 卷 = 总计
- 3 个节点,c5.9xlarge = 36 个 vCPU,每月成本 3300 美元 + 945 美元 = 4245 美元
- 3 个节点,c5.4xlarge = 16 个 vCPU,每月成本 1468 美元 + 945 美元 = 2413 美元
- 5 个节点,c5.4xlarge = 16 个 vCPU,每月成本 2445 美元 + 1575 美元 = 4020 美元
- 7 个节点,c5.4xlarge = 16 个 vCPU,每月成本 4123 美元 + 2205 美元 = 6328 美元
- 5 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1225 美元 + 1575 美元 = 2800 美元
- 7 个节点,c5.2xlarge = 8 个 vCPU,每月成本 1715 美元 + 2205 美元 = 3920 美元
- 9 个节点,c5.2xlarge = 8 个 vCPU,每月成本 2205 美元 + 2835 美元 = 5040 美元
我们不包含数据传输成本。
我们看到,选择 gp2 实际上对我们来说是最便宜的选择。当然,st1 HDD 的每 GB 成本只有其一半,但如果我们选择更小的尺寸,我们就无法实现更高的强度吞吐量。因此,对于我们的需求来说,SSD 似乎可能是最具成本效益的选择。当然,它永远无法处理超过 250 MiB/s 的速度,因此,如果这是限制因素,那么你将被迫选择昂贵的 io1 或 st1。
案例研究
在接下来的文章中,我们将使用镜像队列和仲裁队列执行此规模分析。
可能适用于您工作负载的其他测试
根据您的工作负载,您可能希望运行更多测试。例如
- 您可能希望在峰值负载下运行长时间运行的测试。这两个案例研究中的测试都很短 - 在 10 分钟到 1 小时 40 分钟之间。如果您确定了特定的强度和集群配置,您可以尝试运行 24 小时以确保您的选择。显然,如果您有网络/磁盘突发,那么如果您在峰值期间测试这么长时间,可能会影响结果。
- 您可能需要定期从批处理作业中获取非常大的消息,您需要对此进行测试。顺便说一句,我们建议您使用像 s3 这样的对象存储来存储大型消息(将它的 URI 作为消息传递)。
- 您可能希望通过完全停止和启动集群来测试具有积压的恢复时间。
- 您可能会有数量可变的客户端连接到您的集群。使用正常到最坏情况下的连接客户端数量来测试您的候选集群规模。
- 连接震荡(连接的打开和关闭)也会给集群带来压力。如果您有数量可变的连接震荡,则使用正常和最坏情况下的连接进行测试。另请参阅 /docs/networking.html#tuning-for-large-number-of-connections-tcp-buffer-size。
- 您可能会有数量可变的队列。使用正常到最坏情况下的队列数量来测试您的候选集群规模。
最终想法
您投入规模分析的工作量可能与您在系统中对 RabbitMQ 的依赖程度以及如果它未能提供必要的性能所涉及的成本有关。如果您有一个不重要的较小工作负载,那么您可能不希望在规模分析上花费太多时间。尝试几个较小的选项并进行监控。
如果您有较大的工作负载或业务关键型工作负载,那么花时间正确地进行容量规划和规模分析可能会避免您以后浪费更多时间和精力。
花时间查看仲裁队列和镜像队列的案例研究,但如果您没有时间,那么这里有一些提炼到一个部分的指导原则。
高可用性 (HA) 是一个常见的需求,也是我们使用复制队列(仲裁队列、镜像队列)的原因。我们不希望丢失消息,并且希望即使在发生故障时也能保持持续可用性。不要忘记,容量规划对于实现这些目标也至关重要。
容量规划(Sizing)是指在理想和不利条件下运行您的工作负载或工作负载模拟。峰值负载通常发生在企业盈利最多的时期,而不利条件也最有可能在此期间出现。根据不利条件进行容量规划是正确进行容量规划的关键部分。
RabbitMQ 在适当的容量规划下仍然能够有效运行的不利条件包括:
- 代理节点丢失(磁盘故障、虚拟机重启、网络分区)
- 队列积压(由消费者速度缓慢或发布峰值引起)
- 大量 TCP 连接
- 大量队列
在这些因素中,代理节点丢失可能是 RabbitMQ 承受压力最小的。RabbitMQ 最难处理的事情之一是超大型队列积压。在我们的案例研究中,我们发现,在适当的容量规划下,RabbitMQ 即使在巨大的积压情况下也能处理这种高吞吐量的工作负载,但这仅限于最大的集群。较小的集群在理想条件下运行良好,但在队列开始积压时会迅速恶化。
如果您已经自动化了生产和 QA 环境的部署,那么测试不同的虚拟机大小和数量应该足够简单。如果您能够使用实际应用程序运行这些容量规划测试,那么这很可能为您提供最准确的结果。如果使用自己的应用程序生成负载过于复杂,那么请考虑设计一个尽可能接近现实世界的合成工作负载。
案例研究中包含 PerfTest 命令,并且还有 这篇博文 提供了运行性能测试的指导和选项。
希望这些容量规划指南对您有所帮助,并帮助 RabbitMQ 成为您架构中坚如磐石的一部分。