可靠性指南
什么会导致失败
网络问题可能是最常见的一类故障。
网络不仅可以发生故障,防火墙也可以中断空闲连接,并且不会立即检测到网络故障。
除连接故障外,代理和客户端应用程序可能随时遇到硬件故障(或软件崩溃)。
此外,即使客户端应用程序继续运行,逻辑错误也可能导致通道或连接错误,从而迫使客户端建立新的通道或连接并从问题中恢复。
连接失败
如果连接失败,客户端将需要与代理建立新连接。
在先前连接上打开的任何频道都将自动关闭,这些频道也需要重新打开。
通常,当连接失败时,客户端将通过连接抛出异常(或类似的语言构造)来通知客户端。
官方Java和.NET客户端还提供回调方法,让您了解其他上下文中的连接失败 - Java在Connection类和Channel类上提供ShutdownListener回调,同样的目的,.NET客户端为其提供IConnection.ConnectionShutdown和IModel.ModelShutdown事件
Acknowledgements and Confirms
当连接失败时,消息可能在客户端和服务器之间传输 - 它们可能正在被解析或生成,在OS缓冲区中或在线路上。
传输中的邮件将丢失 - 需要重新传输。
Acknowledgements让服务器和客户知道何时执行此操作。
Acknowledgements可以在两个方向上使用 - 允许消费者向服务器指示它已经接收/处理了消息并允许服务器向生产者指示相同的东西。RabbitMQ将后一种情况称为“ confirm”。
当然,TCP确保已收到数据包,并将重新传输直到它们 - 但这只是网络层。
Acknowledgements和Confirms表明已收到消息并采取行动。
acknowledgement信号表示收到消息,以及所有权转移,接收方承担全部责任的。
因此,Acknowledgements具有语义 - 消费应用程序在完成对它们所需的任何操作之前不应该确认消息 - 将它们记录在数据库中,转发它们,将它们打印到纸上或其他任何东西上。一旦这样做,代理可以自由地忘记该消息。
类似的,代理一旦承担责任就会确认消息。Acknowledgements的使用保证至少一次交付。如果没有确认,则在发布和使用操作期间可能会丢失邮件,并且只保证最多一次交付。
使用心跳检测已死的TCP连接
在某些类型的网络故障中,数据包丢失可能意味着中断的TCP连接需要相当长的时间(例如,在Linux上使用默认配置,大约需要11分钟)才能被操作系统检测到。AMQP 0-9-1提供心跳功能,以确保应用程序层及时发现中断的连接(以及完全没有响应的对端)。心跳还可以防御可能终止“空闲”TCP连接的某些网络设备。
代理
为了避免在代理中丢失消息,我们需要应对代理重启,代理硬件故障以及极端甚至代理崩溃。
为确保消息和代理定义在重新启动后仍然存在,我们需要确保它们位于磁盘上。
AMQP标准具有exchange,队列和持久消息的持久性概念,要求持久对象或持久消息在重新启动后仍然存在。
集群和高可用性
如果我们需要确保我们的代理从硬件故障中存活,我们可以使用RabbitMQ的集群。
在RabbitMQ集群中,所有定义(exchange,绑定,用户等)都在整个集群中进行镜像。
队列的行为方式不同,默认情况下仅驻留在单个节点上,但可选择跨多个或所有节点进行镜像。无论位于何处,队列都可以从所有节点看到并可访问。
镜像队列在所有已配置的集群节点上复制其内容,无缝地容忍节点故障并且不会丢失消息.但是,消费应用程序需要注意,当队列失败时,他们的消费者将被取消,他们将需要重新构建
生产者
使用确认时,从通道或连接失败中恢复的生产者应重新传输尚未从代理收到确认的任何消息。
此处存在消息重复的可能性,因为代理可能已发送了从未到达生产者的确认(由于网络故障等)。
因此,消费者应用程序将需要以幂等方式执行重复数据删除或处理传入消息。
确保消息被路由
在某些情况下,生产者必须确保他们的消息被路由到队列(尽管并非总是如此 - 在pub-sub系统的情况下,生产者只会发布,如果没有消费者感兴趣,那么消息丢弃是正确的)。
为确保将消息路由到单个已知队列,生产者只需声明目标队列并直接发布到该队列即可。如果消息可能以更复杂的方式路由,但生产者仍然需要知道他们是否至少到达一个队列,它可以在basic.publish上设置mandatory标志,如果没有适当的绑定队列 确保basic.return(包含回复代码和一些文本)将被发送回客户端。
生产者还应该意识到,当发布到集群节点时,如果绑定到exchange的一个或多个目标队列在集群中具有镜像,则由于流之间的流量控制,由于副本和主队列进程之间的流量控制,可能会在节点之间的网络故障面临时出现延迟.
消费者
在网络出现故障(或节点崩溃)的情况下,可以复制消息,并且消费者必须准备好处理它们。
如果可能,处理此问题的最简单方法是确保您的消费者以幂等方式处理消息,而不是明确处理重复数据删除。
如果消息被传递给消费者然后重新排队(例如,因为它在消费者连接丢失之前没有得到确认),那么RabbitMQ将在再次传递时为其设置redelivered的标志(无论是同一个消费者还是另一个消费者))。
这是一个消费者之前可能已经看到此消息的暗示(尽管不能保证,消息可能已经从代理中删除但在连接丢失之前没有进入消费者)。
相反,如果未设置redelivered的标志,则保证之前没有看到该消息。因此,如果消费者发现重复删除消息或以幂等方式处理消息更加昂贵,可以通过设置了redelivered标志的消息执行此操作。
消费者取消通知
在某些情况下,服务器需要能够取消消费者 - 因为它正在消耗的队列已被删除或已经故障转移。
在这种情况下,消费者应该再次消费,但要注意它可能再次看到它已经看到的消息。
请注意,消费者取消通知是AMQP的RabbitMQ扩展,因此可能并非所有客户端都支持
无法处理的消息
如果消费者确定它无法处理消息,那么它可以使用basic.reject(或basic.nack)拒绝它,要么服务器重新排队或者不(在这种情况下服务器可能配置为死信)
分布式RabbitMQ
Rabbit提供了两个插件来协助在不可靠的网络上分配节点:federation和shovel。
两者都是作为AMQP客户端实现的,因此如果将它们配置为使用confirms 和 acknowledgements,它们将在必要时重新传输。两者都默认使用confirms 和 acknowledgements。
当使用federation和shovel连接集群时,希望确保federation 链接和shovel容忍节点故障。
federation将自动在下游群集中分发链接,并在下游节点发生故障时将其故障转移。
为了在上游节点发生故障时连接到新的上游,您可以为上游指定多个冗余URI,或者通过TCP负载平衡器进行连接。
使用shovel时,可以在源或目标clause中指定冗余代理;但是目前还不可能使shovel本身冗余。我们希望将来改善这种状况;同时,如果新节点最初运行的节点出现故障,则可以手动启动新节点以运行shovel。