消息队列的幂等性

目录

幂等性

解决方法

让每个消息都会有唯一的消息 id


幂等性

幂等性是数学概念,即 f(x)=f(f(x))。在计算机领域,则是意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。在调用接口时,总有一些特殊情况会导致接口进行重复的调用,如果不对这些情况做出处理,就可能导致脏数据,甚至是业务流程上的问题。

比如,后台管理员在新增数据时,由于特殊原因,比如按钮抖动,而导致目标数据插入两条;用户下单时,没有做逻辑校验,导致用户下了多笔相同的订单。这些情况都是不被允许的,我们要保证这些操作无论执行多少次,最终产生的结果都是相同的,这类业务通常需要拥有较强的一致性。幂等性就是描述这一接口特性的名词。

消息队列的幂等性:一个请求(一条消息),不管重复来多少次,结果是不会改变的。

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。挑一个 Kafka 来举个例子,说说怎么重复消费吧。

Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。

解决方法

如何保证消息队列的幂等性?

产生重复消费的可能原因:消息读写过程中,遇到意外kill进程或重启,导致consumer有些消息处理了,但没来得及提交offset,重启后少数消息会再被消费一次。(offset代表消息的序号,消费过的offset会被提交)

保证MQ的消费幂等性,需要结合具体业务来看,大体思路有:

Redis的写入没有幂等性问题,每次都是set,天然幂等性。

主键或者唯一的id,可以在消费前进行查询一下,是否消费过。

数据库的唯一键可以保证重复数据不会插入多条,重复数据插入时会报错。

让每个消息都会有唯一的消息 id

唯一的消息id有3种:

1)数据库里的主键

2)与主键有相同效果的一个或多个字段组合,例如不可重复的字段,或者几个字段组合起来不可重复

3)特意一个全局唯一的 id,类似订单 id 之类的东西​​​​​​​

当有了唯一的id后,接受消息的程序如何处理消息:

1)先查再保存
每次保存数据的时候,都先查一下,如果数据存在了那么就不保存。这个情况是并发不高的情况。

2)业务表添加约束条件
如果你的数据库将来都不会分库分表,那么可以在业务表字段加上唯一约束条件(UNIQUE),这样相同的数据就不会保存为多份。

3)添加消息表
再数据库里面,添加一张消息消费记录表,表字段加上唯一约束条件(UNIQUE),消费完之后就往表里插入一条数据。因为加了唯一约束条件,第二次保存的时候,MySQL 就会报错,就插入不进去;通过数据库可以限制重复消费。

4)使用 Redis
如果你的系统是分布式的,又做了分库分表,那么可以使用 Redis 来做记录,把消息 id 存在 Redis 里,下次再有重复消息 id 在消费的时候,如果发现 Redis 里面有了就不能进行消费。(与添加消息表类似,只是一个存在数据库,一个存在redis

5)高并发下
如果你的系统并发很高,那么可以使用 Redis 或者 zookeeper 的分布式对消息 id 加锁,然后使用上面的几个方法进行幂等性控制。

发布了524 篇原创文章 · 获赞 80 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/xushiyu1996818/article/details/104234000