前言
我们经常会在项目里面有用到消息队列的场景,比如贷款场景,用户之前在我们这里借了一笔钱,今天把钱还清了,假设这个时候需要发送一个含有推广链接的短信,或者这个用户还钱的时候正好触发了某个规则,可以对这个用户进行额度提升的操作,都可以用消息队列来处理,那么问题来了,
- 消息是如何投递的?
- 消息是否成功的投递到了消息队列中?
- 消息投递过程中是否会有丢失的问题?消息是否被正确消费了呢?
带着上述的几个问题并以rabbitmq为例开始我们今天的主题
消息可靠性
在计算机网络中,一个可靠的通讯协议可以通知发送者它发送的数据是否成功到达消息接收方,那么在rabbitmq中消息是怎样投递的呢?
rabbitmq 消息投递过程
- 生产者生产消息
- 生产者建立与broker的连接
- 生产者发送消息到broker
- broker收到消息,并通过消息中的exchange,routing-key将其路由到指定队列
- consumer监听到指定队列的消息并消费
那么这里我们可以提出几个问题
生产者是否成功地将消息投递到broker?
在默认情况下,rabbitmq 生产者发送完消息后,是不会给rabbitmq返回任何结果的,那很有可能消息发送到一半因为网络或其他原因,消息没有到达broker,消息直接丢失,这种情况是我们无法忍受的,当然rabbitmq也提供了两种方式解决这种问题,供我们自行选择。
- 事务消息模式
- 发送方确认模式
生产者的消息到达broker后,在broker中是否会丢失?
消息到达broker后,很有可能因为一些原因导致broker重启或者退出,因为rabbitmq的消息默认都是在内存里面的,发生重启会导致消息丢失,这也是我们无法忍受的,这里用到了rabbitmq中的持久化机制,需要我们同时保证3个条件,才能保证消息的持久化
- 消息投递时开启了持久化
- 目标交换机开启了持久化
- 目标队列开启了持久化
- 消息是否被消费者成功消费呢?
正常来讲如果消息投递成功了,并且在broker开启了持久化是不是就能保证消息可靠传输了呢?答案是否定的,消费者消费也可能出现问题,比如消费失败,或者消息压根就没有到达消费者(丢了),这个时候又该如何处理呢?RabbitMQ这里提供了一个消费确认的机制,这种机制分为手动确认和自动确认
- manual ack 需要业务方根据消费情况手动确认消息消费情况
- auto ack 消息一经broker发出,就认为已经投递成功了
很显然 autoack这种是有消息丢失风险的,我们很难保证业务方正确消费的情况,所以一般来说我们想要保证消费的可靠性需要开启手动ack模式,确保业务方正确消费后,再进行确认,下面就说一下手动确认在标准AMQP协议中定义的手动ack的接口方法
basic.ack 如果业务方正确消费了消息,那么业务方需要给broker发送一个ack消息,代表消息已经正确处理, broker可以将这条消息删除了
basic.reject 如果业务方发生了异常或者因为某些原因导致无法消费消息,可以给broker发送拒绝消息,让这条消息重新入队,以便让别的消费者可以消费这条消息 对应标准AMQP0.9.1.1协议中的 basic.reject方法
basic.nack 在basic.reject的基础上提供批量拒绝消息的方法
当消息消费失败时候怎么处理?
在原有队列进行几次重试操作,排除网络抖动原因后,将失败消息从原有队列上删除,并加入一个专门处理异常的重试队列以重试的方式进行补偿,必要的时候进行人工处理(若是消息发送方因为某些原因导致数据回滚了,当前消息一定会处理失败,所以需要将对应的消息进行删除,若是因为下游服务不可用导致的,则需要跟业务方进行沟通,如果是业务代码发生了bug,修代码吧兄弟,总之及时看生产的监控消息)