消息重复消费

1、产生重复消费的原因

无论是那种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息RocketMQ是返回一个CONSUME_SUCCESS成功标志kafka offset的概念

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

MQTT中规定了三种传递标准:

At most once:至多一次。消息传递时,最多会被送达一次,没有什么可靠性,允许消息丢失。
At least once:至少一次。消息传递时,至少会被送打一次,保证消息可靠性,但存在多次消费的可能。
Exactly once:恰好一次。消息传递时,只会被送达一次,不允许丢失也不允许重复。

依照以上标准,似乎我们只要保证消息队列符合Exactly once的标准,就可以在保证消息可靠性的前提下解决重复消费的问题。无非就是时间和空间的切换,采用Exactly once标准固然符合要求,但也势必会带来一定的性能损耗,就跟分布式锁类似,而对于At least once,我们则可在业务层面保证数据不会重复消费

在这里插入图片描述

幂等性是指一个操作其执行任意多次所产生的影响均与一次执行的影响相同。比如你同样的参数调用我这个接口,调用多少次结果都相同

kafka产生重复消费的原因:

  1. 强行kill线程,导致消费后的数据,offset没有提交(消费系统宕机、重启等)
  2. 设置offset为自动提交,关闭kafka时,如果在close之前,调用 consumer.unsubscribe()则有可能部分offset没提交,下次重启会重复消费。
  3. (重复消费最常见的原因):消费后的数据,当offset还没有提交时,partition就断开连接。比如,通常会遇到消费的数据,处理很耗时,导致超过了Kafka的session timeout时间(0.10.x版本默认是30秒),那么就会re-blance重平衡,此时有一定几率offset没提交,会导致重平衡后重复消费。
  4. 当消费者重新分配partition的时候,可能出现从头开始消费的情况,导致重发问题。
  5. 当消费者消费的速度很慢的时候,可能在一个session周期内还未完成,导致心跳机制检测报告出问题
  6. 并发很大,可能在规定的时间(session.time.out默认30s)内没有消费完,就会可能导致reblance重平衡,导致一部分offset自动提交失败,然后重平衡后重复消费

2、避免方式

  • 让每个消息携带一个全局的唯一ID(雪花算法、UUID等等),即可保证消息的幂等性,具体消费过程为:
  1. 消费者获取到消息后先根据id去查询redis/db是否存在该消息
  2. 如果不存在,则正常消费,消费完毕后写入redis/db
  3. 如果存在,则证明消息被消费过,直接丢弃。
  • 通过数据库的唯一键实现消息的幂等性

可以在设计消息结构的时候设置一个对应数据库唯一键的列字段,业务成功后将此字段作为唯一键保存入数据库。当同样的ID做保存的时候就会出现违反数据库唯一约束异常,这里的主键可以是单独的,也可以是组合的列。这种方式可以在任何支持“INSERT IF NOT EXIST”的存储系统中适用。

  • 通过版本号/数据快照实现幂等

其实这种方式有点类似于乐观锁的实现方式,就是需要消息中带有此业务当前一个瞬时状态的值,通过这个值与业务当前数据比较来判断是否执行更新操作。

比如:将订单号为00001的状态从01变更为02(当订单00001的状态为01就改为02),这样一条重复的消息进来是不会对00001的订单做任何影响的。此时状态就是一个前置条件。
再比如:将商品A的库存从500中减1(当商品A的总库存为500则减1)。类似这些需要在设计消息结构的时候带上一些业务属性活数据。

另外一种办法就是给消息增加一个类似数据库的version字段,在每次消费更新的时候比较当前数据的版本号是否与消息中带的版本号一致,来判断是否执行消费。

RabbitMQ 是一个功能强大的消息队列中间件,但消息重复消费是一个常见的问题。有几种常见的原因可能导致消息重复消费: 1. 网络问题或连接中断:当消费者从 RabbitMQ 接收到消息后,如果网络出现问题或连接中断,可能会导致消费者无法发送确认消息给 RabbitMQ,从而导致 RabbitMQ 认为该消息没有被正确消费,然后重新将该消息发送给其他消费者。 2. 消费者应用程序异常终止:如果消费者应用程序在处理消息的过程中崩溃或异常终止,RabbitMQ 将重新将该消息发送给其他消费者。 3. 消息处理失败未确认:如果消费者应用程序在处理消息时发生错误,并且没有发送确认消息告知 RabbitMQ 消息处理失败,RabbitMQ 将重新将该消息发送给其他消费者。 为了解决消息重复消费的问题,可以考虑以下几点: 1. 使用消息的唯一标识符:在消息的属性中添加唯一标识符,并在消费者端对已经处理过的消息进行记录。这样,在接收到新消息时,可以先检查该消息是否已经被处理过,避免重复处理。 2. 设置消息的过期时间:可以为每条消息设置一个过期时间,在一定时间内未被消费者处理,则认为该消息已过期,不再重新发送给其他消费者。 3. 使用幂等性操作:在消费者处理消息的过程中,尽量使用幂等性操作,即使同样的消息被多次处理,也不会对系统状态造成影响。 4. 异常处理和消息确认:在消费者应用程序中,及时捕捉并处理异常情况,并在消息处理完成后发送确认消息给 RabbitMQ,确保消息被正确消费。 这些是一些常见的解决方案,根据实际情况选择适合的方法来避免消息重复消费
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值