MQ消息重复消费问题
可能出现重复消费的场景
- 网络抖动
- 消费者挂了
MQ通用的解决方案
- 每条消息设置一个唯一的标识id
- 幂等方案:【 分布式锁、数据库锁(悲观锁、乐观锁) 】
RabbitMQ的死信交换机
当一个队列中的消息满足下列情况之一时,可以成为死信(dead letter):
- 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
- 消息是一个过期消息,超时无人消费。其又可以细分为两种情况(以最短的时间超时为准)
- 消息所在的队列设置了存活时间
- 消息本身设置了存活时间
- 要投递的队列消息堆积满了,最早的消息可能成为死信
如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机(Dead Letter Exchange,简称DLX)。
ATT:死信交换机实际上也就是一个普通的交换机,它能在任何队列被指定,只不过赋予了接受死信的功能而已。所以需要在定义队列时就指定死信交换机。
@Bean
public Queue ttlQueue(){
return QueueBuilder.durable("simple.queue")//指定队列名称并持久化
.ttl(10000)//设置队列的超时时间,10秒
.dealLetterExchange("dl.direct")//指定死信交换机
.build();
}
RabbitMQ的延迟队列(DelayExchange)
延迟队列:进入队列的消息会被延迟消费的队列
场景:超时订单、限时优惠、定时发布
延迟队列本质还是官方的三种交换机,只是添加了延迟功能。
实现延迟队列可以用以下两种方法之一:
- 队列绑定一个死信交换机,在死信交换机上可以绑定其他队列,在我们发消息的时候可以按照需求指定TTL的时间。(本质上是死信交换机+TTL)
- 安装一个DelayExchange插件,声明一个交换机,然后设定delayed属性为true即可。
RabbitMQ官方的插件社区地址为:https://www.rabbitmq.com/community-plugins.html
不需要掌握,本人留档使用
问题与回答模板
RabbitMQ消息的重复消费问题如何解决的?
回答:(背熟以下回答,大概用时1min)
嗯,这个我们还真遇到过,是这样的,我们当时消费者是设置了自动确认机制,当服务还没来得及给MQ确认的时候,服务宕机了,导致服务重启之后,又消费了一次消息。这样就重复消费了
因为我们当时处理的支付(订单|业务唯一标识),它有一个业务的唯一标识,我们再处理消息时,先到数据库查询一下,这个数据是否存在,如果不存在,说明没有处理过,这个时候就可以正常处理这个消息了。如果已经存在这个数据了,就说明消息重复消费了,我们就不需要再消费了
面试官:那你还知道其他的解决方案吗?
候选人:
嗯,我想想~
其实这个就是典型的幂等的问题,比如,redis分布式锁、数据库的锁都是可以的。
RabbitMQ中死信交换机或者说延迟队列有了解过嘛?
回答:(背熟以下回答大概用时1min)
首先关于死信的定义,一般满足以下情况之一,就可能成为死信。譬如,过期时间到了却仍未被消费的消息,或者要投递的队列满了,最早的消息就可能成为死信。死信交换机实际上就是设置的一个用来接收死信的交换机,它与普通的交换机其实没有什么区别。
而延迟队列,一般用于超时订单、限时优惠、定时发布之类的场景。本质还是官方的三种交换机之一,只是添加了延迟功能。我们实现它可以有两种方式。第一种就相当于死信交换机加上TTL,第二种更简单一些,就是去官网下载相关的插件,然后定义时把delay设置为true即可。