消息的可靠性
面试问题:
RabbitMQ如何保证消息可靠性?|| RabbitMQ如何保证消息不丢失?
1、RabbitMQ发送与消费消息的模型
2、消息丢失的几种情况?
1、生产者发送消息未到达交换机
2、消息到达交换机,没有正确路由到队列
3、MQ宕机,队列中的消息不见了
4、消费者收到消息,还没消费完,消费者宕机
3、如何保证消息不丢失?
1、生产者确认机制
1、publisher-confirm机制 消息成功投递到交换机,返回ack 消息未成功投递到交换机,返回nack 如果失败,就要重新发送
实现:定义ConfirmCallback
ConfirmCallback可以在发送消息时指定,因为每个业务处理confirm成功或失败的逻辑不一定相同。
2、publisher-return机制 未正确到达队列,返回ack及失败原因
实现:定义Return回调
每个RabbitTemplate只能配置一个ReturnCallback,因此需要在项目加载时配置。
2、持久化机制
一般来说 交换机、队列以及消息都是默认开启持久化的 但是有时候需要开启消息持久化的话
在发送消息时,使用Message对象,并设置delivery-mode为持久化
3、消费者ack机制
ack取值
- none:只要消息到达消费者,Spring直接返回ack到MQ
- MQ收到ack,会把队列中的消息删除
- 消息会丢失
- 消费者配置 - spring: rabbitmq: listener: simple: acknowledge-mode: none # 关闭ack
- manual:手动ack
- 消费成功,调用API给MQ返回ack
- 消费失败,调用API给MQ返回nack,并且让消息重回队列
- 消费者配置 - spring: rabbitmq: listener: simple: acknowledge-mode: manual #手动ack
auto:自动ack【默认值】。消费消息不出异常,返回ack给MQ。消费消息出异常了,返回nack,把消息重回队列 -
1、本地重试
- 消费者在消费消息时,如果失败了,则在本地重试,重新消费,如果达到重试次数,还是失败,则返回ack,不重回队列,MQ会删除队列中的消息
- spring: rabbitmq: listener: simple: retry: enabled: true #开启消费者失败重试
initial-interval: 1000 #初始的失败等待时长为1秒
multiplier: 2 #失败的等待时长倍数,下次等待时长 = multiplier * last-interval
max-attempts: 3 #最大重试次数
stateless: true #true无状态;false有状态。如果业务中包含事务,这里改为false
2、失败策略
- 1、RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
- 2、ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
- 3、RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
3、使用 RepublishMessageRecoverer - 需求:把消息投递到失败的交换机,路由队列。记录日志,将来人工干预
消息重复消费问题
面试问题
同一个消息如何保证重复消费问题?|| 消息如何保证幂等性?
什么是幂等性?
多次执行同一个操作,最终的结果是一样的
幂等
查询
根据条件删除
第一次,删除成功
第二次及以后,虽然数据已经不存在了,但是删除操作也是成功的,只是没有删除数据。所以也不影响
更新
update 表名 set 字段=值 where id = ?
非幂等性
增加
多次执行,会插入多少数据
更新
update 表名 set 字段=字段 - 值 where id = ?
保存订单、扣减库存等操作
1. 对于幂等性操作,多次消费消息,除开性能的影响 ,其他没有什么大问题,可以不管它
2.对于非幂等性操作,多次消费消息,会造成数据一致性的问题,所以要保证重复消费消息的问题
解决方法
-
唯一标识符和去重:在生产者端或消息内容中添加唯一标识符,用于标识每条消息的唯一性。消费者在处理消息前,先检查该标识符是否已经处理过相同的消息,如有则进行去重操作。
-
消费者端幂等性:设计消费者端的处理逻辑具有幂等性。即无论消息被处理多次,最终结果都保持一致。这样,即使消息被重复消费,也不会对最终结果产生影响。
-
消费者确认模式(手动应答):在消费者处理消息后,通过显式发送确认(acknowledgement)给RabbitMQ,确认消息已经被成功处理。RabbitMQ在收到确认后,才会将消息从队列中删除。如果消费者在处理消息过程中发生错误,可以选择不发送确认,使消息重新投递给其他消费者。 -消息超时设置:在消息中设置一个合理的超时时间。如果消费者未能及时处理消息,超过超时时间后消息会被重新投递给其他消费者,避免了消息长时间占据队列而无法被处理的问题。
-
监控和日志记录:建立完善的监控系统,及时检测和记录消息消费的状态和异常情况。通过监控和日志记录,可以对消息的消费情况进行追踪和分析,及时发现重复消费问题并进行处理。
消息积压问题
1、产生的原因?
生产者生产消息的速度 远高于 消费者消费消息的速度?于是就会造成消息积压在MQ中
2、分析为什么会有消息积压?
1、设计是否有问题?
如果是,重新设置生产者与消费者数量匹配
如
一个生产者
多个消费者
优化架构
2、消费者出问题?
1、消费者出异常了
修改消费者代码,让消费者正常工作
2、消费者宕机了
第一步:修复宕机的情况
第二步:临时开启多个消费者,来以多倍速消费积压的消息。当积压的消息消费的差不多的情况,关闭临时消费者
3、惰性队列
可以放很多消息,还可以把多的消息持久化到本地
死信
产生原因
1.消费者使用basic.reject 或 basic.nack声明消费失败,并且消息的requeue参数设置为false
2.消息是一个过期消息,超时无人消费
3.要投递的队列消息满了,无法投递