跳到主要内容

RabbitMQ 中的高可用性:解决难题的一部分

·4 分钟阅读
Matthew Sackman

在 RabbitMQ 2.6.0 中,我们引入了高可用性队列。 这些需要对 AMQP 进行新的扩展,以及相当多的文档,但到目前为止,关于它们如何工作的文章很少。

高可用性 (HA) 是一个通常被过度使用的术语,对不同的人来说意味着不同的事物。在 RabbitMQ 的上下文中,高可用性 有许多方面,其中一些方面这项工作确实解决了,而另一些则没有解决。 它没有解决的问题包括

  1. 维护与 RabbitMQ Broker 或节点的连接:使用某种 TCP 负载均衡器或代理是这里的最佳途径,尽管其他解决方案,例如动态更新 DNS 条目或只是预先加载您的客户端以连接到的地址列表也可能同样有效。

  2. 从故障中恢复:如果客户端由于连接到的节点发生故障而与 Broker 断开连接,如果客户端是发布客户端,Broker 可能已经接受并传递了来自客户端的消息,而客户端没有收到确认;同样,在消费端,客户端可能已经发出了消息确认,但不知道这些确认是否已到达 Broker 并在故障发生之前得到处理。 简而言之,您仍然需要确保您的消费客户端可以识别和处理重复消息。

  3. 从网络分区或分裂中自动恢复。 RabbitMQ 使用 Erlang 分布式数据库 Mnesia。 这个数据库本身不能应对网络分区:它在 CAP 三角形中非常选择 一致性可用性,而不是 分区。 由于 RabbitMQ 依赖于 Mnesia,RabbitMQ 本身也具有相同的属性。 因此,RabbitMQ 中的 HA 工作可以防止队列在节点故障时消失,但对于在修复后自动重新加入故障节点没有任何作用:这仍然需要手动干预。

这些根本不是新问题;RabbitMQ 的 HA 工作并没有试图解决这些问题。相反,它只专注于防止队列绑定到集群中的单个节点。

以前的情况是队列仅存在于一个节点上。 如果该节点发生故障,则队列变得不可用。 HA 工作通过在其他节点上镜像队列来解决这个问题:队列主节点上发生的所有操作都会被拦截,并以相同的顺序应用于镜像中的每个从节点。

这需要

  1. 拦截在队列上执行的所有操作的能力。 幸运的是,我们已经拥有的代码抽象使这变得相当容易。

  2. 将这些操作可靠、一致且按顺序地传达给镜像中的所有从节点的能力。 为此,我们编写了一个新的 保证多播 模块(也称为原子广播)。

  3. 可靠地检测节点丢失的能力,以确保来自该节点的消息不会到达从节点的子集:为了确保镜像队列的成员彼此保持同步,至关重要的是,在主节点发生故障的情况下,主节点正在发送到从节点的任何消息要么完全失败,要么完全成功(这实际上是原子广播中的原子性)。

此外,镜像成员之间的所有这些通信都以异步方式进行。 这具有一些优点,例如它可以防止主节点在其中一个从节点开始挣扎时变慢;但它也有缺点,例如在主节点故障和从节点升级的情况下,操作交错的复杂性。

一旦主节点确实发生故障,就会选择一个从节点进行升级。 选择的从节点是最老的从节点,因为我们认为它最有可能拥有与故障主队列内容匹配的内容。 这很重要,因为目前没有对镜像队列进行主动同步。 因此,如果您创建一个镜像队列,向其中发送消息,然后添加另一个节点,然后该节点镜像该队列,则新节点上的从节点将不会收到现有消息。 只有发布到队列的新消息才会被发送到镜像队列的所有当前成员。 因此,通过从队列中消费并处理队列头部的消息,非完全镜像的消息将被消除。 因此,通过升级最老的从节点,您可以最大限度地减少队列头部可能只有故障主节点知道的消息数量。

© . All rights reserved.