RabbitMQ 获得高可用性升级
这是关于仲裁队列(我们新的复制队列类型)系列文章的第一部分。我们将涵盖从仲裁队列是什么,到硬件要求,从镜像队列迁移以及最佳实践等所有内容。
介绍仲裁队列
镜像队列,也称为 HA 队列,多年来一直是需要额外数据安全保证消息的事实上的选择。仲裁队列是下一代复制队列,旨在取代镜像队列的大多数用例,并且从 3.8 版本开始可用。
在本博客系列中,我们将介绍以下内容
- 第 1 部分 - 了解对新复制队列类型的需求以及其工作原理(本文)
- 第 2 部分 - 为什么选择存储驱动器对于仲裁队列很重要
- 第 3 部分 - 仲裁队列和流量控制。概念。
- 第 4 部分 - 仲裁队列和流量控制。单队列基准测试。
- 第 5 部分 - 仲裁队列和流量控制。压力测试。
- 第 6 部分 - 仲裁队列和集群/虚拟机大小调整 (即将推出)
- 第 7 部分 - 何时以及如何从镜像队列迁移到仲裁队列 (即将推出)
- 第 8 部分 - 最佳实践和注意事项 (即将推出)
- 第 9 部分 - 监控和管理操作 (即将推出)
为什么提供新型复制队列?
镜像队列基于称为链式复制的复制算法。在队列的上下文中,链式复制形成一个队列环,其中有一个领导者(主节点)和一个或多个从节点(镜像)。所有消息都发布到领导者并从领导者消费,然后领导者将其操作复制到其相邻的镜像,镜像再复制到其相邻的镜像。这将持续到到达最后一个镜像,然后最后一个镜像通知主节点操作是否已完全复制。
由于某些导致消息丢失的极端情况,此算法进行了修改,以便通道进程还可以将消息直接发送到每个镜像。不幸的是,这意味着从发布者接收消息的代理必须发送两次每条消息,这会使网络负载加倍。
此复制算法还存在一些痛点,这些痛点集中在它如何处理由于服务器重启、节点故障或网络分区而离开环的镜像。
当镜像与同级的通信时间超过一定时间限制时,它将从环中移除,并且队列将继续可用。问题发生在镜像再次加入环时。首先,镜像会丢弃其所有数据,然后,可以选择开始一个称为同步的过程。
同步是指主节点将其当前消息复制到镜像。这是一个“停止世界”的过程,队列将被冻结,直到同步完成。如果队列非常大,这就会成为一个问题,因为不可用期可能很长。
另一种选择是不将重新加入的镜像与主节点同步。在这种情况下,我们最终会获得较低的冗余性,但避免了可能很痛苦的同步。当然,如果队列为空或消息很少,则同步不会构成大问题。
另一个重要主题是它如何处理网络分区。当发生将集群分成两半的分区时,我们将最终得到一个或多个与主节点失去通信的镜像。作为管理员,我们可以选择在此处选择可用性或一致性。cluster_partition_handling 配置确定 RabbitMQ 如何处理分区。
如果我们不想丢失消息,那么我们将配置集群以使用 pause-minority 模式。这基本上会停止分区少数一方的所有代理。在多数一方(如果存在)上,队列将继续运行,只是冗余性降低了。一旦分区解决,集群就会恢复正常。此策略选择一致性(尽管冗余性较低)而不是可用性。
如果我们希望分区两侧都保持可用性,则可以选择 ignore 或 auto-heal 模式。这将允许镜像被提升为主节点,这意味着我们在两侧都有一个主节点。这允许队列继续接收和传递消息,无论客户端连接到哪一方。不幸的是,在解决分区时,将选择分区的一侧作为受害者并重新启动,丢失该侧队列中的所有消息。这不是理想的选择,但在某些情况下,它仍然比变得不可用更适合您的需求。
我们需要一个更好的复制算法,这就是仲裁队列的诞生。
仲裁队列
仲裁队列 不使用链式复制,而是基于完善且经过数学证明的 Raft 协议。Raft 是一种用于在节点集群中复制操作日志的共识算法。它要求大多数参与节点可用并同意附加到分布式日志的每个新操作。这就是仲裁队列名称的由来。
队列有哪些操作?我们有 入队 操作和 消费者确认 操作。与镜像队列一样,所有客户端都与领导者交互,领导者的工作是将入队和确认复制到其跟随者。
该算法更有效率,并且可以实现比镜像队列更高的吞吐量。它的端到端延迟较高,并且这些延迟与磁盘的吞吐量/延迟密切相关。仲裁队列仅在写入大多数磁盘后才确认消息,因此磁盘性能在仲裁队列性能中发挥着重要作用。
无需艰难抉择
没有“停止世界”同步,没有在重新加入时丢弃数据,根本不需要对自动同步与手动同步做出艰难的决定。无需在可用性和一致性之间做出选择;仲裁队列只有在消息已复制到大多数节点后才会确认消息。如果大多数节点都宕机,那么您将失去可用性。
网络分区
在网络分区方面,仲裁队列要简单得多。首先,它们使用单独且速度更快的故障检测器,可以快速检测分区并触发快速领导者选举,这意味着可用性不受影响或很快恢复。cluster_partition_handling 配置不适用于仲裁队列,尽管 pause_minority 模式仍然会影响仲裁队列,因为当暂停少数一方时,该节点上托管的任何仲裁队列领导者都将变得不可用。但是,由于故障检测器的速度很快,因此在发生分区的情况下,领导者选举应该会在触发此暂停之前很久就选择了新的领导者。
仲裁队列适用于数据安全至上的场景。但请记住,数据安全始于应用程序正确执行操作,正确使用发布者确认和消费者确认。仲裁队列确实有一些限制和注意事项,我们将在本系列的后面部分讨论这些内容。
x-queue-type:经典和仲裁
现在我们有了新型队列,我们将队列称为 经典 队列或 仲裁 队列。经典队列与以前的队列相同。使用 ha-mode、ha-params、ha-sync-mode 属性声明经典队列将使其成为经典镜像队列,定义具有这些参数并与队列匹配的策略也会使其成为经典镜像队列。
但是,您不能通过策略将经典队列转换为仲裁队列。必须在队列声明中包含设置为 quorum 的 x-queue-type 属性。它从诞生之日起就是仲裁队列。
管理 UI 中的仲裁队列
在下图中,首先您会注意到 x-queue-type: quorum 参数和 x-quorum-initial-group-size: 3 参数,它们表明此队列是复制因子为 3 的仲裁队列(一个领导者和两个跟随者)。
图 7. 管理 UI 中显示的仲裁队列我们还可以看到队列的成员、哪些成员在线以及当前领导者是谁。
声明仲裁队列与声明任何其他队列一样简单,只需使用 x-queue-type 参数即可。默认情况下,仲裁大小(复制因子)为 5,但在较小的集群中,大小将与集群本身的大小相同。
在管理 UI 中,您将看到添加经典队列或仲裁队列的选项。
选择仲裁时,您会看到可用参数发生变化。
仲裁队列有三个新的可用参数。其中两个与内存中保留的消息数量有关。默认情况下,所有消息都保存在内存中,因此如果仲裁队列长度增长,它可能会给集群带来内存压力。我们可以通过设置以下一个或两个参数来限制消息使用的内存量
- x-max-in-memory-length
- x-max-in-memory-bytes
然而,队列索引仍然存储在内存中,因此队列大小持续增长仍然会给集群带来内存压力。我们将在本系列的后面更详细地讨论这一点。
新的毒消息功能仅适用于仲裁队列,并使用x-delivery-limit参数设置。每次消息重新传递给消费者时,都会递增一个计数器。一旦重新传递次数超过x-delivery-limit,消息就会被丢弃或进入死信队列(如果已配置DLX交换机)。
要点
仲裁队列提供了可靠的安全保证,并且在重启服务器时减少了麻烦。与镜像队列相比,它们对硬件施加了不同的压力,因此在进行切换之前,请查看我们的下一篇文章,其中包含有关迁移和所需硬件的指导。
在下一篇文章中,我们将介绍为什么我们推荐使用SSD而不是HDD,以及每种存储驱动器类型带来的不同性能特征。