出现原因

正常情况下在consumer真正消费完消息后应该发送ack,通知broker该消息已正常消费,从queue中剔除
当ack因为网络原因无法发送到broker,broker会认为词条消息没有被消费,此后会开启消息重投机制把消息再次投递到consumer。

消费模式:在CLUSTERING模式下,消息在broker中会保证相同group的consumer消费一次,但是针对不同group的consumer会推送多次

消息重复的原因

消息领域有⼀个对消息投递的QoS定义,分为:

  • 最多⼀次(At most once)
  • ⾄少⼀次(At least once)
  • 仅⼀次( Exactly once)

⼏乎所有的MQ产品都声称⾃⼰做到了At least once。既然是⾄少⼀次,那避免不了消息重复,尤其是在分布式⽹络环境下。⽐如:⽹络原因闪断,ACK返回失败等等故障,确认信息没有传送到消息队列,导致消息队列不知道⾃⼰已经消费过该消息了,再次将该消息分发给其他的消费者。
不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送⼀个ACK确认消息,RocketMQ是返回⼀个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念。
RocketMQ没有内置消息去重的解决⽅案,最新版本是否⽀持还需确认。

消息去重

1)去重原则:使⽤业务端逻辑保持幂等性

幂等性:就是⽤户对于同⼀操作发起的⼀次请求或者多次请求的结果是⼀致的,不会因为多次点击⽽产⽣了副作⽤,数据库的结果都是唯⼀的,不可变的。
只要保持幂等性,不管来多少条重复消息,最后处理的结果都⼀样,需要业务端来实现。

2)去重策略:保证每条消息都有唯⼀编号(⽐如唯⼀流⽔号),且保证消息处理成功与去重表的⽇志同时出现。
建⽴⼀个消息表,拿到这个消息做数据库的insert操作。给这个消息做⼀个唯⼀主键(primary key)或者唯⼀约束,那么就算出现重复消费的情况,就会导致主键冲突,那么就不再处理这条消息。