如何保证消息不被重复消费:

名词解释

幂等性: 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。

RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。即不是通过配置MQ使用它自带的机制来保证,而是通过开发人员通过代码来保证,以kafka为例进行说明。

kafka:

kafka消息消费机制:

kafka使用offset的概念。在每个消息写进去时,都会生成一个offset代表消息的序号。当consumer消费了数据后,每隔一段时间(定时定期)将消费过的消息的offset提交,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

可能失败的场景:

有时候系统紧急重启,直接用kill 强制杀掉进程,导致consumer 的消费过的offset 没来得及提交。此时 kafka记录的offset还是上次而不是最新的。就会导致未提交的offset的消息重新消费了。

RocketMq:

RocketMq的消息图:

kafka怎么避免重复消费呢 kafka如何防止消息重复消费_kafka

可能失败的场景总结:
  1. 生产者重复发送:消息链路过长,A系统发消息给B系统,B系统发消息给MQ, B系统消息发送成功后,由于网络原因或者其它原因导致没能成功发送确认消息给A系统,导致A系统以为发送失败,重新发送。
  2. 生产者重复发送:A系统发消息给MQ,MQ正常接收后,因为网络原因或者其它原因导致确认消息发送失败,A收不到ack会重新发送消息。
  3. 消费者重复消费:消费者消费消息成功后,在给MQ发送消息确认的时候出现了网络异常,或者如kafka和RocketMq 一样发送最新offset 给MQ时,被重启了导致没成功更新消费者的offset,导致下一次消费者会重新消费。

保证幂等性的几个思路:

保证消息的幂等性,关键是得由开发人员自己控制。
在生产者端可以通过查询mq(rocketMq支持)消息是否存在和写入redis进行记录来控制MQ消息重复发送问题,但是在高并发情况下存在性能消大,且无法保证消费端不重复消费。所以最好的方案是在消费端进行MQ消息重复消费控制。包括但不限于以下两种思路,可以根据不同的业务采用:

  1. mq消息处理时有存入数据库: 在插入数据库时根据主键或者业务字段判断下当前消息数据是否在数据库中已存在,不存在才插入。
  2. mq消息处理时不存入数据库:设置消息的全局唯一的 id,将已消费过的消息的唯一id存到redis中,以后每次处理消息时都去redis中查询一下,判断数据是否已被消费过。