跳到主要内容

第6章 RabbitMQ可靠性

1.生产者消息确认

理想情况下消息投递的流程是:

生产者 -- > 交换机 -- > 队列 < -- 消费者

但是在意外的情况下,这中间每个环节可能都会出现问题从而导致消息投递失败,比如:

  • 生产者发送消息到达MQ后,未找到交换机
  • 生产者发送消息到交换机后,未找到合适的队列

针对这类情况,RabbitMQ提供了生产者确认机制,包含了Publisher ConfirmPublisher Return两种。

  • Publisher Confirm:ack和nack
  • Publisher Return:return

注意

开启生产者确认是比较消耗MQ性能的,一般不建议开启,因为上述的情况一般都是开发者配错了,或者是MQ配置错了。

2.消费者消息确认

默认情况下消费端是自动确认的,也就是说只要消费端从队列里接收到了消息,就会自动返回ACK确认,告诉消息队列消息已经被消费了,然后队列收到ACK确认后就会自动删除这个消息。

image-20240218165029573.png

但是这样可能会导致一个问题,就是虽然消费端取到了消息,但是负责处理业务逻辑的代码出错了,没有正确热按成业务功能,但是消费者确实正常接收到了,又因为默认为自动确认ACK,所有队列里当然就没有这条消息了。

image-20240218165259922.png

基于以上的场景我们会发现自动确认并不一定靠谱,为了确认消费者是否成功处理消息,Rabbit提供了消费者确认机制,即消费者处理结束后,应该向RabbitMQ发送一个确认消息,告诉RabbitMQ这条消息的处理状态,消息状态有三种:

  • ack:消息成功处理,RabbitMQ会从队列中删除该消息
  • nack:消息处理失败,RabbitMQ需要再次投递消息
  • reject:消息处理失败并拒绝该消息,RabbitMQ也会从队列中删除该消息

reject方式一般不会使用,工作中我们一般处理就是如果成功返回ack如果失败返回nack

3.失败重试机制

刚才我们设置了手动确认ack消息,如果业务处理失败了会发送nack消息,然后RabbitMQ会将消息重新投递到队列,但是如果这时候我们代码一直处理失败,那就会导致RabbitMQ无限的重试,从而导致服务器负载变高。

所以我们可以通过设置对应的配置来限制最大重试次数:

spring:
rabbitmq:
listener:
simple:
retry:
enabled: true # 开启消费者失败重试
initial-interval: 1000ms # 初识的失败等待时长为1秒
multiplier: 1 # 失败的等待时长倍数,下次等待时长 = multiplier * last-interval
max-attempts: 3 # 最大重试次数
stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为false

效果:

如果重试3次失败之后,就会抛出异常,此时RabbitMQ会删除消息,说明返回的状态是reject

4.失败处理策略

虽然限制了重试次数,但是最终消息还是被丢弃了,这显示并不是理想的处理方案。

所以在代码层面,Spring框架允许开发者自定义重试次数超过了之后的处理策略,这个策略是由MessageRecovery接口来实现,有三种不同的策略:

  • RejectAndDontRequeueRecoverer:重试次数超过后直接reject丢弃消息,默认这个模式
  • ImmediateRequeueMessageRecoverer:重试次数超过后返回nack,重新加入队列
  • RepublishMessageRecoverer:重试次数超过后,将失败消息投递到指定的交换机

比较优雅的处理方案是RepublishMessageRecoverer,将失败的消息专门投递到一个指定的队列,后续由人工来处理

更新: 2024-10-04 18:47:20