可靠性指南
概述
本指南概述了 RabbitMQ 的功能以及(部分)支持的与数据安全和故障处理相关的协议。
它们有助于应用程序开发人员和运维人员实现可靠的交付,即确保消息在遇到各种故障时也能始终被传递。
数据安全是 RabbitMQ 节点、发布者和消费者共同的责任。因此,本指南概述了消息传递系统中每个部分的重要主题。
本指南主要是概述。每个主题将在其自己的专用指南中进行更详细的讨论。
以下指南将更详细地讨论数据安全和弹性主题
什么可能出错?
消息传递系统本质上是分布式的,可能以各种不同且有时微妙的方式出现故障。
网络连接问题和拥塞可能是最常见的故障类别。网络不仅会发生故障,防火墙可能会中断它们认为空闲的连接,并且网络故障需要时间来检测。
除了连接故障之外,服务器和客户端应用程序随时可能遇到硬件故障(或软件崩溃)。此外,即使客户端应用程序仍在运行,逻辑错误也可能导致通道或连接错误,迫使客户端建立新的通道或连接并从问题中恢复。
当然,这个故障列表并不详尽。它不包括更微妙的故障,例如遗漏故障(在可预测的时间内未响应)、性能下降、耗尽系统资源的恶意或有缺陷的应用程序等等。这些故障可以通过监控、指标和健康检查来检测。
连接故障
在客户端和 RabbitMQ 节点之间的网络连接发生故障时,客户端需要与代理建立新连接。先前连接上打开的任何通道都将自动关闭,需要重新打开。
通常,在连接失败时,客户端会通过连接抛出异常(或类似的语言结构)来收到通知。
大多数客户端库提供自动从连接故障中恢复的功能。对于不适合这种预设恢复的情况,应用程序开发人员可以通过定义连接失败事件处理程序来实现自己的恢复。请参阅客户端文档,例如Java和.NET 客户端指南,以了解更多信息。
确认与应答
当连接失败时,消息可能在客户端和服务器之间传输 - 它们可能正在被解码或编码,停留在 TCP 堆栈缓冲区中,或者在电线上飞行。在这种情况下,传输中的消息将不会被传递 - 需要重新传输。确认让服务器和客户端知道何时执行此操作。
确认可以双向使用 - 允许消费者向服务器指示它已收到和/或处理了传递,并允许服务器向发布者指示相同内容。它们分别称为消费者确认和发布者确认。
虽然 TCP 确保数据包已传递到连接的对端,并且会一直重传直到传递为止,但这只处理网络层的故障。确认表明消息已由对端应用程序收到并采取了行动。确认信号不仅表明收到了消息,还表明所有权的转移,接收方承担全部责任。
因此,确认具有语义。消耗应用程序不应确认消息,直到它完成了对消息的所有操作:将其记录在数据存储中、转发或执行任何其他操作。一旦完成,代理就可以标记传递以便删除。
同样,代理将在承担消息责任后确认消息。详细信息包含在确认与应答指南中。
使用确认可以保证至少一次传递。没有确认,在发布和消耗操作过程中可能会丢失消息,并且只能保证最多一次传递。
使用心跳检测死掉的 TCP 连接
在某些类型的网络故障中,数据包丢失可能意味着中断的 TCP 连接需要相当长的时间(例如,在 Linux 上使用默认配置大约 11 分钟)才能被操作系统检测到。AMQP 0-9-1 提供心跳功能,以确保应用程序层及时了解中断的连接(以及完全无响应的对端)。心跳还可以防御某些可能会终止“空闲”TCP 连接的网络设备。有关详细信息,请参阅关于心跳的指南。
RabbitMQ 端的安全数据
为了避免在 RabbitMQ(而非应用程序)端丢失消息,队列和消息必须能够应对 RabbitMQ 节点重启、节点和硬件故障。
在 RabbitMQ 支持的某些消息传递协议中,应用程序控制队列和消息的持久性。因此,对于重要数据使用持久化队列(或下面介绍的复制队列类型)至关重要,并且消息由发布者作为持久化消息发布。
集群和队列内容复制
节点集群提供冗余并能容忍单个节点的故障。在 RabbitMQ 集群中,所有定义(交换器、绑定、用户等)都会复制到整个集群。
复制队列、流和超流(分区流)是复制的数据结构。其中一个节点托管一个领导者副本,其他副本是跟随者。在领导者失败的情况下,会选举其中一个跟随者成为新的领导者。队列状态更改(入队、跟踪传递和确认)发生在领导者副本上,尽管某些操作也可以在跟随者上执行。
无论其领导者副本位于哪个节点,队列和流对所有节点都保持可见和可访问。在领导者重新选举期间,对于复制队列,正在进行的的消息传递将暂停,直到选出新的领导者。在成功选举领导者的情况下,这对客户端来说是透明的。
独占队列与它们的连接生命周期绑定,因此永远不会被复制,并且定义上不会在节点重启后存活。
连接到故障节点的消费者将需要像往常一样恢复。RabbitMQ 会在为队列选举出新的领导者副本时,自动重新注册连接到不同节点的消费者。这些消费者不需要执行恢复(例如重新连接或重新订阅)。
发布者端的安全数据
在使用确认时,从通道或连接故障中恢复的生产者应重传任何未收到代理确认的消息。这里存在消息重复的可能性,因为代理可能已发送了确认,但由于网络故障等原因从未到达生产者。因此,消费者应用程序需要执行去重或以幂等方式处理传入的消息。
确保消息被路由
在某些情况下,生产者确保其消息被路由到队列很重要(尽管并非总是如此 - 在发布/订阅系统中,生产者只需发布,如果没有消费者感兴趣,消息被丢弃是正常的)。
为了确保消息路由到单个已知队列,生产者可以直接声明目标队列并直接发布到它。如果消息可能以更复杂的方式路由,但生产者仍需要知道它们是否至少到达一个队列,它可以设置 basic.publish 上的 mandatory 标志,确保如果消息没有被正确绑定到任何队列,则会发送一个 basic.return(包含回复代码和一些文本说明)回客户端。有关详细信息,请参阅发布者指南。
生产者还应注意,在发布到集群节点时,如果绑定到交换器的一个或多个目标队列在集群中有镜像,则在节点之间发生网络故障时,可能会由于副本之间的流量控制和队列领导者副本而导致延迟。有关更多详细信息,请参阅节点间心跳指南。
消费者端的安全数据
在网络故障(或节点故障)的情况下,消息可能会被重新传递,消费者必须准备好处理它们以前收到的传递。建议消费者实现设计为幂等的,而不是显式执行去重。
如果一条消息被传递给一个消费者然后被重新排队(无论是 RabbitMQ自动还是由同一或不同消费者执行),RabbitMQ 将在再次传递时设置 redelivered 标志。这是一个提示,表明消费者可能之前已经看到过此消息。这并不保证,因为原始传递可能由于网络或消费者应用程序故障而未能到达任何消费者。
如果未设置 redelivered 标志,则保证该消息以前从未被见过。因此,如果消费者发现去重消息或以幂等方式处理消息的成本更高,则可以仅对设置了 redelivered 标志的消息执行此操作。
无法处理的传递
如果消费者确定它无法处理某条消息,则可以使用 basic.reject 或 basic.nack 方法拒绝该消息,可以选择要求服务器重新排队,或者不重新排队(在这种情况下,服务器可以配置为死信)。
消费者取消通知
当消费者正在消费的队列被删除时,RabbitMQ 将通知消费者。此类消费者必须采取措施进行恢复,无论是从不同的队列消费还是在安全且适当的时候重新声明其最初正在消费的队列。
联邦(Federation)和铲(Shovel)
RabbitMQ 提供了两个插件来帮助在不可靠的网络(如广域网)上分发节点:联邦(Federation)和铲(Shovel)。两者都会从网络故障中恢复并在必要时重新传输消息。两者默认都使用确认和应答。
在使用联邦(Federation)或铲(Shovel)连接集群时,希望确保联邦链接和铲(Shovel)能够从节点故障(包括永久性(故障停止)场景)中恢复。
联邦(Federation)将自动将链接分发到下游集群,并在下游节点故障时进行迁移。为了在发生上游节点故障时连接到新的上游,必须为上游指定多个上游 URI,或者必须通过具有足够可用性特征的负载均衡器进行连接。
铲(Shovel)可以使用多个源端点和目标端点;将使用第一个可达的端点。失败的铲(Shovel)将在可配置的延迟和重试后重新启动。
监控和健康检查
某些故障场景微妙且难以观察或检测。例如,一个缓慢的连接泄漏会随着时间的推移而累积,就像慢性病一样,在一段时间内不为人知。监控和指标是检测多种类型故障的方式。使用Prometheus等工具收集的长期指标数据可以帮助发现系统行为中的异常和问题模式。
除了监控之外,健康检查是另一个可用于检测即时问题的工具,即在当前时刻可观察到的问题。广泛的健康检查覆盖可能导致误报,因此更多的检查并不总是更好。
监控和健康检查都在专用指南中进行了介绍。