跳至主内容
版本:4.3

可靠性指南

概述

本指南概述了 RabbitMQ 及其支持的部分协议中与数据安全和故障处理相关的功能。

它们旨在帮助应用程序开发者和运维人员实现可靠投递,即确保消息始终能够被成功投递,即使在遇到各种类型的故障时也不例外。

数据安全是 RabbitMQ 节点、发布者消费者共同的责任。因此,本指南概述了基于消息系统的每个部分所需关注的主题。

本指南主要为概览性质。每个主题都在其专门的指南中进行了更详细的讨论。

以下指南详细讨论了数据安全和弹性相关主题:

可能发生哪些故障?

基于消息的系统本质上是分布式的,可能会以不同且有时细微的方式发生故障。

网络连接问题和拥塞可能是最常见的故障类型。不仅网络会中断,防火墙可能会中断它们认为处于空闲状态的连接,并且网络故障需要一定时间才能被检测到

除了连接故障外,服务器和客户端应用程序随时可能遇到硬件故障(或软件崩溃)。此外,即使客户端应用程序保持运行,逻辑错误也可能导致通道(channel)连接(connection)错误,迫使客户端必须建立新的通道或连接并从问题中恢复。

当然,这个故障列表并不详尽。它没有涵盖更细微的故障,例如遗漏故障(在预期时间内未响应)、性能下降、消耗系统资源导致性能耗尽的恶意或有缺陷的应用程序等。这些故障可以通过监控、指标和健康检查来检测。

连接故障

如果客户端和 RabbitMQ 节点之间的网络连接失败,客户端将需要与代理建立新的连接。在旧连接上打开的所有通道都将被自动关闭,因此也需要重新打开。

通常当连接失败时,客户端会通过抛出异常(或类似的语言构造)收到通知。

大多数客户端库都提供自动从连接故障中恢复的功能。对于这种预设的恢复机制不适用的情况,应用程序开发者可以通过定义连接故障事件处理器来实现自己的恢复逻辑。请参阅客户端文档,例如 Java.NET 客户端指南以了解更多信息。

确认与发布确认

当连接失败时,消息可能正处于客户端和服务器之间的传输过程中——它们可能正在两端进行解码或编码,驻留在 TCP 栈缓冲区中,或是在网络线路上传输。在这种情况下,传输中的消息将无法投递,需要重新发送。确认机制(Acknowledgements)让服务器和客户端知道何时执行此操作。

确认可以双向使用:允许消费者向服务器表明它已接收和/或处理了投递的消息,并允许服务器向发布者表明同样的事实。它们分别被称为消费者确认(Consumer Acknowledgements)和发布确认(Publisher Confirms)。

虽然 TCP 确保数据包已传送到连接对端并会进行重传直到成功,但这仅处理了网络层的故障。确认机制表明消息已被对端应用程序接收并处理。确认不仅标志着消息的接收,还标志着所有权的转移,即接收方承担了处理该消息的全部责任。

因此,确认机制具有特定的语义。消费应用程序在对消息执行完所需操作(例如记录到数据存储、转发消息或执行任何其他操作)之前,不应确认消息。一旦执行完毕,代理便可以标记该投递的消息以进行删除。

同样地,代理在承担了消息的责任后会进行确认。详细信息请参阅确认与发布确认指南

使用确认机制可保证至少一次(at least once)投递。如果没有确认机制,在发布和消费操作期间可能会丢失消息,此时仅能保证最多一次(at most once)投递。

通过心跳检测失效的 TCP 连接

在某些类型的网络故障中,数据包丢失可能意味着中断的 TCP 连接需要相当长的时间(例如在 Linux 上默认配置下约为 11 分钟)才能被操作系统检测到。AMQP 0-9-1 提供了一种心跳功能,以确保应用层能够及时发现中断的连接(以及完全无响应的对端)。心跳还可以防御某些可能终止“空闲”TCP 连接的网络设备。详情请参阅心跳指南

RabbitMQ 侧的数据安全

为了避免在 RabbitMQ(而非应用程序)侧丢失消息,队列和消息必须能够应对 RabbitMQ 节点的重启、节点和硬件故障。

通过 RabbitMQ 支持的某些消息协议,应用程序可以控制队列和消息的持久性。因此,对于重要数据使用持久化队列(或下文介绍的副本队列类型),并由发布者将消息发布为持久化至关重要。

集群与队列内容复制

节点集群提供冗余,并能容忍单个节点的故障。在 RabbitMQ 集群中,所有定义(交换机、绑定、用户等)都会在整个集群中复制。

仲裁队列(Quorum Queues)流(Streams)和超级流(分区流)是复制数据结构。其中一个节点托管领导者副本(leader replica),其他则是追随者(followers)。如果领导者发生故障,将从追随者中选举出新的领导者。队列状态的变更(入队、跟踪投递和确认)均在领导者副本上进行,尽管某些操作也可以在追随者上执行。

无论领导者副本位于哪个节点,队列和流对于所有节点来说都是可见且可达的。在领导者重选期间,对于仲裁队列,正在进行的消息投递将会暂停,直到选出新的领导者。如果领导者选举成功,这一过程对客户端是透明的。

排他队列(Exclusive queues)绑定在连接的生命周期上,因此永远不会被复制,且按定义不会在节点重启后存活。

连接到故障节点的消费者必须按惯例进行恢复。连接到其他节点的消费者,当队列选出新的领导者副本时,将被 RabbitMQ 自动重新注册。这些消费者无需执行恢复操作(如重新连接或重新订阅)。

发布者侧的数据安全

当使用发布确认时,从通道或连接故障中恢复的生产者应重新发送所有尚未收到代理确认的消息。此处存在消息重复的可能性,因为代理可能已经发送了确认但生产者并未收到(由于网络故障等原因)。因此,消费者应用程序需要执行去重,或以幂等方式处理传入的消息。

确保消息被路由

在某些情况下,确保消息被路由到队列对生产者而言可能很重要(尽管并非总是如此——在发布-订阅系统中,生产者只需发布消息,如果没有消费者关注,丢弃消息是正确的做法)。

要确保消息被路由到单个已知队列,生产者可以直接声明目标队列并直接发送消息。如果消息可能以更复杂的方式路由,但生产者仍需知道它们是否到达了至少一个队列,则可以在 basic.publish 上设置 mandatory 标志,以确保在没有绑定任何适当队列的情况下,会向客户端发送一个 basic.return(包含回复代码和文字说明)。详细信息请参阅发布者指南

生产者还应注意,当发布到集群节点时,如果绑定到交换机的一个或多个目标队列在集群中拥有镜像,由于队列领导者副本和副本之间的流控,在节点间出现网络故障时可能会产生延迟。详情请参阅节点间心跳指南

消费者侧的数据安全

在发生网络故障(或节点故障)时,消息可能会被重新投递,消费者必须准备好处理过去已经见过的投递。建议将消费者实现设计为幂等,而不是显式地执行去重。

如果消息被投递给消费者后又被重新入队(由 RabbitMQ 自动执行,或由相同/不同的消费者执行),RabbitMQ 在再次投递该消息时会设置 redelivered 标志。这是一种提示,表明消费者可能之前见过此消息。但这并非绝对保证,因为原始投递可能由于网络或消费者应用程序故障并未到达任何消费者。

如果未设置 redelivered 标志,则可以保证该消息之前从未被见过。因此,如果消费者发现去重或以幂等方式处理消息的成本很高,它可以仅针对设置了 redelivered 标志的消息进行处理。

不可处理的投递

如果消费者确定无法处理某条消息,则可以使用 basic.rejectbasic.nack 方法拒绝它,并选择是否要求服务器重新入队(如果不重新入队,服务器可以配置为将其发送到死信队列)。

消费者取消通知

当消费者正在消费的队列被删除时,RabbitMQ 将通知消费者。此类消费者必须采取行动进行恢复,无论是消费其他队列,还是在安全且适当的情况下重新声明原队列。

联邦(Federation)与搬运工(Shovel)

RabbitMQ 提供了两个插件来协助在不可靠网络(如广域网)上分发节点:联邦(Federation)搬运工(Shovel)。两者都能从网络故障中恢复,并在必要时重新传输消息。两者默认都使用发布确认和确认机制。

当使用联邦或搬运工连接集群时,确保联邦链接和搬运工能够从节点故障(包括永久性停机场景)中恢复是非常必要的。

联邦会自动在下游集群中分发链接,并在下游节点故障时进行迁移。为了在上游节点故障时连接到新的上游,必须为上游指定多个上游 URI,或者连接必须通过具有足够可用性特征的负载均衡器进行。

搬运工可以使用多个源和目标端点;将使用第一个可达的端点。失败的搬运工将在可配置的延迟和重试后重启。

监控与健康检查

某些故障场景比较细微,难以观察或检测。例如,缓慢的连接泄漏可能会随时间积累,像慢性病一样在一段时间内未被察觉。监控与指标是检测多种类型故障的方法。使用诸如 Prometheus 等工具收集的长期指标数据,有助于发现系统行为中的不规则性和问题模式。

除了监控之外,健康检查是另一种可用于检测即时(point-in-time)问题的工具,即此刻可观察到的问题。过多的健康检查覆盖范围可能会导致误报,因此检查项并非越多越好。

监控和健康检查都在专门指南中进行了介绍。

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