公众号merlinsea
消息重复消费问题介绍
当消费者获取到消息并已经完成消费了以后,会发送ack给rabbitmq,然后rabbitmq就会将该消息标记为已经消费完成。但由于存在网络问题,消费者发送ack的时候可能存在发送失败,导致rabbitmq认为该消息没有成功被消费者消费,之后就会进行重新投递,导致消费者会多次收到同一条消息的可能性。
【由于网络问题,任何消息队列都可能存在消息重复消费的问题!!!】
如何解决消息重复消费的问题,即如何保证消息消费时的幂等性?
答:由于网络抖动的问题,需要在消费者端保障消息消费时候的幂等性。
方法1:每次消费者收到消息的时候都将消息的id存入redis中,如果redis中已经存在这个消息的id了,说明是二次消费,则可以直接返回ack给消息队列,如果redis中不存在这个消息ID,那么说明是第一次消费,则可以进行消费,消费完成后就可以将消息存入redis,同时返回ack。
//Redis中操作,判断是否已经操作过 TODO boolean flag = jedis.setNX(key); if(flag){ //消费 }else{ //忽略,重复消费 }
方法2:建立一个数据库去重表,这个表的字段就是message id,并将该字段作为唯一索引。这里是利用数据库的唯一索引不可重复的方法来判断之前是否消费过该消息。
说明点:
是否需要添加额外的redis或者数据库表来保障消息消费时候的幂等性是看业务场景而定。比如说在某种业务场景下,消费者每次收到消息都是需要将数据库的某条记录的state改为“finish”,那么不论这条消息来多少次,其实【最终结果都是一致的】,因此也没必要额外引入redis来保障幂等性。但在有些场景下如果消费者每次监听到消息会导致业务的【最终结果每次都发生改变】,那么就需要单独引入redis保障消费消息时的幂等性。
update coupon_record set state='FINISH' where id =#{id}