RabbitMQ的几个高频面试题

132 阅读5分钟

消息的可靠性

面试问题:

RabbitMQ如何保证消息可靠性?|| RabbitMQ如何保证消息不丢失?

1、RabbitMQ发送与消费消息的模型

image.png

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为持久化

image.png

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.要投递的队列消息满了,无法投递