天天看点

突破Java面试-如何保证消息消费时的幂等性?何时重复消费?怎么保证消息队列消费的幂等性?

消费消息需要考虑:

  • 会不会重复消费
  • 能不能避免重复消费
  • 重复消费了也别造成系统异常可以吗

使用MQ如何保证幂等性也是架构设计考虑的问题。

rabbitmq、rocketmq、kafka,都可能会出现消费重复消费,因为这个问题不是MQ自身保证的,是我们开发自己需要保证的。

何时重复消费?

kafka有个offset概念,每个消息写进去,都有一个offset,代表他的序号,然后consumer消费了消息后,每隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的offset来继续消费。

但凡事有意外,比如重启系统,碰到急的,直接kill进程再重启。这会导致consumer有些消息处理了,但是没来得及提交offset。重启之后,少数消息会再次消费。

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,如何保证幂等性。

比如你有个系统,消费一条往DB插一条,要是你一个消息重复两次,你不就插入两条,这数据不就错了?

但你要是消费到第二次时,自己判断一下已消费了,直接扔了,不就只保留了一条数据!

一条数据重复出现两次,DB里就只有一条数据,这就保证了系统的幂等性。

幂等性,就一个数据或一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。

所以

怎么保证消息队列消费的幂等性?

还是得结合业务来思考,大体思路如下:

  • 写DB,你先根据主键查一下,如果这数据都有了,你就别插入了,update之
  • 写redis,那没问题了,反正每次都是set,天然幂等
  • 其它场景,你需要让生产者发送每条消息时,里面加一个全局唯一id,然后你这里消费到了之后,先根据这个id去redis查一下,之前消费过吗?
    • 如果没有消费过

      你就处理,然后这个id写redis

    • 如果消费过了

      那你就别处理了,保证别重复处理相同的消息

还有比如基于DB的唯一键保证重复数据不会重复插入多条。