RabbitMQ-消息可靠性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Soongp/article/details/87445540

消息的可靠性体现在两个层面:消息发送的可靠性和消息消费的可靠性。

下文解释了如何使用AMQP和RabbitMQ的各种特性来实现可靠的传递——确保消息始终被传递,即使在系统的任何部分遇到故障。

What Can Fail?

网络问题可能是最常见的故障类型。不仅网络会失败,防火墙会中断空闲Connection,而且网络故障并不总是立即检测到。

除了Connection失败之外,代理(服务端broker)和客户端(生产或消费)应用程序在任何时候都可能出现硬件故障(或软件崩溃)。此外,即使客户端应用程序继续运行,逻辑错误也会导致通道或连接错误,从而迫使客户端建立新的通道或连接并从问题中恢复。

Connection Failures

在连接失败的情况下,客户端需要建立到代理(服务端broker)的新连接。上一个(失败的)连接打开的任何通道都将自动关闭,(所以)这些通道也需要重新打开。

通常,当连接失败时,客户端将通过抛出异常(或类似的语言结构)的连接得到通知。官方的Java和.net客户端还提供回调方法来让您了解其他上下文中的连接失败——Java在Connection类和Channel类上都提供了ShutdownListener回调。出于同样的目的.net客户端提供IConnection.ConnectionShutdown IModel.ModelShutdown。

Acknowledgements and Confirms

准确地说,Consumer Acknowledgements and Publisher Confirms

当Connection失败时,消息可能在客户端和服务器之间传输——它们可能在被解析或生成的过程中、在OS缓冲区中或在连接上。传输中的消息可能会丢失—它们需要重新传输。ack可以让服务器和客户端知道何时执行此操作(消息重发)。

ack会应用在2个层面——①允许消费端向服务端指示它已接收/处理了消息,即Consumer Acknowledgements。②允许服务器向生产者指示相同的事情,即Publisher Confirms。RabbitMQ将后一种情况②称为 "confirm"("确认")。

当然,TCP确保数据包已被接收,(若没有)将重新传输,直到它们被接收为止---但这只是网络层。ack和confirm表明已接收到消息并已对其进行处理。

因此,ack具有语义—消费应用程序在完成对消息的任何处理(将消息记录在数据库中、转发到数据库中、打印到纸上或其他任何地方)之前不应该(返回)确认消息(basicAck)。一旦这样做,代理就可以自由地忘记消息。

使用ack保证至少一次交付。如果没有ack,消息可能在发布和消费操作期间丢失,并且最多只能保证一次交付(就是也可能一次不保证)。

Detecting Dead TCP Connections with Heartbeats

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

At the Broker

我们需要处理broker重启、broker硬件故障,甚至在极端情况下broker崩溃等情况以避免在代理中丢失消息。

为了确保消息和代理能够在重新启动时恢复,我们需要确保它们的信息保存到磁盘上。AMQP标准对exchange、queue的持久性和持久消息的持久性都有一个概念,它要求持久对象或持久消息在重启后能够存活。有关持久性和持久性的特定标志的详细信息,可以在AMQP概念指南中找到(http://www.rabbitmq.com/tutorials/amqp-concepts.html)。

Clustering and High Availability

如果需要确保broker在硬件故障中幸存,可以使用RabbitMQ的集群。在RabbitMQ集群中,所有定义(交换器、绑定关系binding、用户等)都在整个集群中进行镜像。队列的行为不同,默认情况下仅驻留在单个节点上,但可以跨多个或所有节点进行镜像。无论队列位于何处,所有节点都可以看到和访问队列。

镜像队列在所有配置的集群节点之间复制它们的内容,无缝地容忍节点故障,并且没有消息丢失(非同步的镜像上请参阅http://www.rabbitmq.com/ha.html#unsynchronised-mirrors)。然而,消费端需要知道,当队列失败时,它们的消费者将被取消,并且需要重新消费——有关更多细节,请参阅文档(http://www.rabbitmq.com/ha.html#behaviour)。

At the Producer

在使用confirm时,从channel或connection失败中恢复的生产者应重新发送尚未从broker收到确认的任何消息。这里存在消息重复的可能性,因为代理可能发送了一个confirm确认消息但是从未到达生产者(由于网络故障等)。因此,消费者程序将需要以幂等方式处理重复传入的消息或对消息滤重。

Ensuring Messages are Routed

在某些情况下,对于生产者来说,确保将其消息路由到队列是很重要的(尽管并不总是如此——对于发布订阅模式,生产者只发布消息,如果没有消费者感兴趣,那么删除消息是正确的)。

为了确保消息被路由到单个已知队列,生产者只需声明一个目标队列并直接发布到它。如果消息可能以更复杂的方式路由,但是生产者仍然需要知道它们是否到达了至少一个队列,那么它可以在basic.publish方法上设置mandatory标志(设置为true)。如果没有适当绑定队列,确保basic.return方法将此信息返回给客户端(生产者)。

生产者也应该意识到,当其发布(消息到)一个集群节点,如果一个或多个绑定到交换器的目标队列在集群中镜像,面对节点间网络故障可能会导致延迟,由于主从节点之间的队列过程的Flow Control。详情

At the Consumer

在网络故障(或节点崩溃)的情况下,消息可能会重复,消费者必须做好避免重复消费的准备。如果可能,最简单的处理方法是确保您的消费者程序以幂等方式处理消息,而不是显式地删除重复数据。

如果消息已经传递到消费者,但是被重入队列(例如,因为它在消费者连接被丢弃之前没有被ack确认),然后RabbitMQ将在它再次被传递时对其设置redelivered标志(无论是传递到相同的消费者还是不同的消费者)。这暗示消费者以前可能见过此消息(虽然不能保证,即在连接断开之前,该消息可能已经从代理发出,但是没有投递给消费者)。相反,如果没有设置redelivered标志,则可以保证以前没有看到该消息。因此,如果使用者发现消除重复消息或幂等处理重复消息的开销更大,则只能对设置了redelivered标志的消息执行此操作。

Consumer Cancel Notification

在某些情况下,服务端需要能够取消一个消费者——因为它正在消费的队列已经被删除,或者已经失败。在这种情况下消费者再次消费时要注意,它可能会再次看到它已经看到的消息。请注意,消费者取消通知是AMQP的RabbitMQ扩展,因此可能不支持所有客户端。

Messages That Cannot Be Processed

如果消费者确定它不能处理消息,那么它可以使用basic.reject(或basic.nack)拒绝它,要么请求服务器将它重入队列,要么不请求(在这种情况下,服务器可能被配置为dead-letter)。

通过上述这些特性,我们就可以通过一些方式去做消息失败的补偿(写前日志+定时补偿),但是要保证消息消费的幂等性(数据库唯一主键或消费行为本身就满足幂等性)。

猜你喜欢

转载自blog.csdn.net/Soongp/article/details/87445540