如何保证消息消费的幂等性

或者说,如何保证消息消费的幂等性?

业务场景

在本项目中,新增员工接口,会有邮件发送,在测试接口的过程中,我们可能会有重复增加的操作,相对应的消费者端会收到两个邮件。

但是我们的用户已经收到了入职邮件。

显然这种情况,我们在生产中是不希望发生的。(会被员工在背后骂的,这SB,真么又来了!)为了避免挨骂,我们还是要做点处理的。

专业点的说,用户对于同一操作发起的一次请求或者多次请求的结果是一致的。

更新了这张表的数据,那么这个条件就不生效了,也就不会执行操作了,通过这种乐观锁的机制来保障幂等性.

Consumer(消费端)幂等性

消费端实现幂等性,就意味着,我们的消息永远不会消费多次,即使我们收到了多条一样的消息。

在业务高峰期最容易产生消息重复消费问题,当Con(消费者)消费完消息时,在给Pro(生产者)返回 ack 时由于网络中断,导致Pro(生产端)未收到确认信息,该条消息就会(被我们的定时任务)重新发送并被Con(消费者)消费,但实际上该消费者已成功消费了该条消息,这就造成了重复消费。

主流幂等性实现方案

唯一ID +指纹码机制,利用DB主键去重。

SELECT COUNT(1) FROM T_ORDER
        WHERE ID = 唯一ID +指纹码
  • 唯一ID:业务表的主键
  • 指纹码:为了区别每次正常操作的码,每次操作时生成指纹码;可以用时间戳+业务编号或者标志位(具体视业务场景而定),在本业务中我们使用的是UUID。

这个方案,实现简单。但是在高并发的情况下,有数据库写入的瓶颈。但是也有解决方案,根据ID进行分库分表算法路由。

这个在高并发的解决方案,还要继续研究。

在本项目的管理系统,其实高并发的情况很少,我们就采用这种方案来避免消费端的幂等性。

具体的实施流程:

  • 首先我们需要根据消息生成一个全局唯一ID,然后还需要加上一个指纹码。这个指纹码它并不一定是系统去生成的,而是一些外部的规则或者内部的业务规则去拼接,它的目的就是为了保障这次操作是绝对唯一的。
  • 将ID + 指纹码拼接好的值作为数据库主键,就可以进行去重了。即在消费消息前呢,先去数据库查询这条消息的指纹码标识是否存在,没有就执行insert操作,如果有就代表已经被消费了,就不需要管了

相关的方案

需要考虑的问题:

  • 是否要进行数据落库,若落库,数据库和缓存如何做到原子性(缓存(redis是一致性的那和关系型数据库合并)和数据库的一致性的问题)
  • 若不落库,那么都存储到缓存中,如何设置定时同步策略(如何将缓存中的数据同步到数据库中去)

这里只提用Redis的原子性去解决MQ幂等性重复消费的问题

MQ的幂等性问题 根本在于的是生产端未正常接收ACK,可能是网络抖动、网络中断导致

Con在消费开始时将 ID放入到Redis的BitMap中,Pro每次生产数据时,从Redis的BitMap对应位置若不能取出ID,则生产消息发送,否则不进行消息发送。

但是有人可能会说,万一Con,ProRedis命令执行失败了怎么办,虽然又出现重复消费又出现Redis非正常执行命令的可能性极低,但是万一呢?

OK,我们可以在Redis命令执行失败时,将消息落库,每日用定时器,对这种极特殊的消息进行处理。

Original: https://www.cnblogs.com/bearbrick0/p/16150143.html
Author: BearBrick0
Title: 如何保证消息消费的幂等性

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/685785/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球