如何保证消息不被重复消费,或者说如何保证消息幂等性。

rbbitmq,rocketmq,kafka,都有可能出现消息重复消费的情况,以kafka来说
kafka实际上有个offset的概念,就是每个消息写进去,都有一个offset,代表的消息的序号,然后consumer消费了数据之后,每隔一段时间(定期定时),会把自己消费过的offset提交一下,表示“我已经消费过了,下次重启之后,还是从上次消费到的offset来继续消费。

如果遇到意外情况,还没来得及提交offset,进程就被杀掉了,重启之后,可能有些消息要重复消费一次。

实例;

数据1/2/3依次进入kafka,kafka会给这三条数据分配一个offset,代表这条数据的序号,假设分配的offset依次是152/153/154。消费者从kafka去消费的时候,也是按照这个顺序去消费的,假如当消费者消费了 offset= 153这条数据,刚准备提交offset到zookpeeper,此时消费者进程被重启了,那么此时消费过的数据1/2的offset并没有提交,kafka也就不知道你已经消费了offset=153,这条数据,那么重启之后,消费者会找kafka说,嘿,哥们儿,你给我接着把上次我消费的那个地方的数据继续给我传递进来,由于之前的offset没有提交成功,那么数据1/2数据会再次传进来,如果此时消费者没有去重的话,会导致重复消费。

多消费者 消息队列 时序性 消息队列重复消费问题_rbbitmq

如果消费者是拿一条数据就往数据库里写一条,会可能你把1/2数据在数据库里面插入了两次,数据就错了。

重复消费不可拍,重要的是你要考虑重复消费之后怎么保证消息的幂等性。
幂等性,通俗的说,就一个数据,或者一个请求,给你重复来多次,你的确保对应的数据是不会改变的,不能出错。

如何保证消息队列消息的幂等性?

具体思路:
  • 比如你拿个数据要写库,你先根据主键检查一下,如果这数据都有了,update一下就好了。
  • 比如你写入redis,那没问题了,反着每次都是set,天然幂等性
  • 如果你不是上面两个场景,你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单id之类的东西,然后你这里消费到了之后,先根据id去比如redis(数据库)查一下,之前消费过了吗,如果没有消费,就处理,然后写入reids或者库中,如果消费过了,就不要处理了。
  • 比如基于数据库的唯一健来保证重复数据不会插入多条,因为有唯一健约束,重复数据插入只会报错,不会导致数据库出现脏数据
  • 多消费者 消息队列 时序性 消息队列重复消费问题_rocketmq_02