RabbitMQ消息模式(消息100%的投递、幂等性概念)

1、什么是生产端的可靠性投递

  • 保障消息的成功发出
  • 保障MQ节点的成功接收
  • 发送端收到MQ节点(Broker)确认应答
  • 完善的消息进行补偿机制

2、BAT/TMD互联网大厂解决生产端的可靠性投递的方案

  • 消息落库,对消息状态进行打标,流程图如下:

  • 消息的延迟投递,做二次确认,回调检查,流程图如下:

3、幂等性是什么

可以参考数据库乐观锁机制,比如执行一条更新库存的 SQL 语句,在并发场景,为了性能和数据可靠性,会在更新时加上查询时的版本,并且更新这个版本信息;可能你要对一个事情进行操作,这个操作可能会执行成百上千次,但是操作结果都是相同的,这就是幂等性

如:

Update t_repository set count = count -1,version = version + 1 where version = 1

Elasticsearch也是严格遵循幂等性概念,每次数据更新,version+1

4、消费端的幂等性保障

在海量订单生成的业务高峰期,生产端有可能就会重复发生了消息,这时候消费端就要实现幂等性,这就意味着我们的消息永远不会被消费多次,即使我们收到了一样的消息

5、业界主流的幂等性操作

  • 唯一ID+指纹码机制,利用数据库主键去重

如:

Select count(1) from T_order where ID = 唯一ID+指纹码

为什么需要指纹码呢?这是为了应对用户在一瞬间的频繁操作,这个指纹码可能是我们的一些规则或者时间戳加别的服务给到的唯一信息码,它并不一定是我们系统生成的,基本都是由我们的业务规则拼接而来,但是一定要保证唯一性,然后就利用查询语句进行判断这个id是否存在数据库中

好处:实现简单

坏处:高并发下有数据库写入的性能瓶颈

解决方案 :根据ID进行分库分表,对ID进行算法路由,落到一个具体的数据库,然后当这个ID第二次来又会落到这个数据库,这时候就像我单库时的查重一样了;利用算法路由把单库的幂等变成多库的幂等,分摊数据流量压力,提高性能

  • 利用Redis的原子性去实现

使用Redis进行幂等,需要考虑的问题:

一是是否要进行数据落库,如果落库的话,关键解决的问题是数据库和缓存如何做到原子性? 数据库与缓存进行同步肯定要进行写操作,到底先写redis还是先写数据库,这是个问题,涉及到缓存更新与淘汰的问题

二是如果不落库,那么都存储到缓存中,如何设置定时同步的策略? 不入库的话,可以使用双重缓存等策略,保障一个消息副本,具体同步可以使用类似databus这种同步工具

6、进一步理解幂等性

比如rabbitmq、rocketmq、kafka,都有可能会出现消费重复消费的问题,因为这问题通常不是mq自己保证的,是给你保证的。

我们挑一个kafka来举个例子,说说什么情况下会重复消费吧:kafka实际上有个offset的概念,就是每个消息写进去,都有一个offset,代表他的序号,然后consumer消费了数据之后,每隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了,下次要是重启啥的,你就让我继续从上次消费到的offset来继续消费吧

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接kill进程了,再重启;这会导致consumer有些消息处理了,但是没来得及提交offset,这样重启之后,少数消息会再消费一次;其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性

例如:假设你有个系统,消费一条往数据库里插入一条,要是你一个消息重复消费两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下已经消费过了,直接扔了,不就保留了一条数据?一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性

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

发布了121 篇原创文章 · 获赞 82 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_42687829/article/details/104327711