【RabbitMQ 】RabbitMQ高级特性之《消息可靠性保障与消息幂等性保障--乐观锁机制》

系列文章:
RabbitMQ 系列文章

【RabbitMQ 】自动补偿机制(消息重试、重发机制)
RabbitMQ高级特性之《消息可靠性保障与消息幂等性保障–乐观锁机制》 由消息重试引发多次发送消息,导致幂等性问题

概述

RabbitMq没有给出具体的解决方案,需要用户自行设计

首先一般为了保证消息能够被正确的消息,当消费者处理过程中出现异常,没有正确的返回ack时,会采用例如消息补偿机制(消息重试),再次发送消息,因此,可能会引发业务被多次调用的情况,进而需要解决幂等性:

dosomething()

dosomething2()

抛出异常

此时消息会重发,但是dosomething()已经被调用了,如果消息重发,会多次调用dosomething()

消息补偿参见 自动补偿机制(消息重试、重发机制)

1. 消息可靠性保障

在RabbitMQ的使用过程中我们如何保证消息百分百从生产端发送到服务端呢,在淘宝,银行等系统中是不允许任何错误的,任何消息的不可达都可能会造成巨大的损失。

我们通过发送端消息确认机制confirm机制和return机制确保消息能发送出去,通过接收端的Acknowledge (ACK)确保队列中的消息被处理了,但是如果保证发送的完整链上的消息被处理了,比如服务端发送成功后,接收端尚未接收,队列重启了,消息丢失。

下面来教大家一个解决方案,即消息补偿机制

在这里插入图片描述
这个图看起来好像很复杂,其实只做到了三件事情:

  • 发消息
  • 收到消息确认
  • 检查比对是不是收到消息了

1.当发生业务操作的时候,业务数据写入数据库
2.生产者将消息发送给MQ的队列Q1
3.发送了一条与step2中一摸一样的延迟消息给Q3
4.Consumer监听Q1,获取到了step2中发送的业务消息
5.消费者在收到生产者的业务消息后,发送了一条确认消息(记录收到的消息信息)到Q2
6.回调检查服务监听了Q2,获取到了消费者发送的确认消息
7.回调检查服务将这条确认消息写入数据库等待之后的比对

8.Q3中的延迟消息延迟时间已到,被回调检查服务接收到,之后就拿着这条延迟消息在数据库中比对,如果比对成功,证明消费者接收到了生产者的业务消息并处理成功(如果不处理成功谁会傻了吧唧发送确认消息呢);如果比对失败,证明消费者没有接收到生产者的业务消息,或者说消费者接收到了业务消息之后始终没有处理成功相关的业务并发送确认消息。这时回调检查服务就会调用生产者的相关业务接口,让生产者再次发送这条失败的消息

9.有一种最极端的情况,step2和step3的消息都发送失败了或者说在消息传递过程中发生意外丢失了!定时检查服务会一直轮询保存确认消息的数据库中的消息数据,并于生产者的业务数据库中的业务数据进行比对,如果两者比对数量一致,则代表业务执行没有问题;如果比对不一致,确认消息数据库的数据量小于生产者业务数据量的话,就证明消费者没有接收到生产者发送的消息。这时定时检查服务会通知生产者再次发送消息到MQ的队列Q1

核心思想是定时任务进行核对。

2. 消息幂等性

  • 幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
  • 在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果。

首先,无论是RabbitMQ、RocketMQ还是kafka,都有可能出现消息的重复发送,这个是MQ无法保障的,而幂等性是开发或者运维人员需要保证的。

所谓消息的幂等性是指即使收到多次消息,也不会重复消费,这点很重要,例如用户付钱,点的太快了,付了多次,但是你也只能扣一次钱。

比如说转账案例:我第一次通过手机银行发送的转账500申请由于网络或者说银行系统负载高等原因,导致没有执行成功,我个人又点了很多次转账的按钮,但是银行的系统必须保证我只转出去了500。

2.1 RabbitMQ可能导致出现非幂等性的情况

1、可靠性消息投递机制:consumer回复confirm出现网络闪断,producer没有收到ack,定时任务轮询可能就会重新发送消息,这样consumer就会收到两条消息

2、MQ Broker与消费端传输消息的过程出现网络抖动

3、消费端故障或异常

2.2 解决方案

关于解决幂等性问题的方案有很多,此处配合RabbitMQ的消息补偿机制就介绍一下常见的乐观锁机制,数据库中乐观锁的运用就不做过多解释了,相信看这篇文章的小伙伴都应该知道哈。

在刚才所讲的消息补偿机制中,如果消费端除了某种问题导致无法获取到业务数据会触发以下两种补偿机制:

  • 当确认消息发送失败,回调检查服务会用延迟消息比对数据库中是否存在确认消息,如果没有会通知生产者继续发送同样的业务数据
  • 当定时检查服务检查确认消息小于业务数据的时候也会调用生产者重发业务消息到MQ

这时候队列Q1中可能会有多条一摸一样的消息,当消费者的服务恢复正常之后,Q1队列中这些一样的消息都会被Q1读取并进行业务处理。此时如果消费者没有幂等性的保证的话,就会出现“多扣钱”的问题。

此时乐观锁就派上用场了,通过每次消息中带有的version信息,来判断该业务操作是否能执行成功:

在这里插入图片描述

-- 消息内容:
{id = 1, money = 500, operation = subtract, version = 1}
 
-- 第一次执行
update account set money = money - 500, version = version + 1 where id = 1 and version = 1;
 
--第二次执行的时候,version = 2,所以这条sql无法匹配到数据就不做扣钱处理了
update account set money = money - 500, version = version + 1 where id = 1 and version = 1;

针对每个操作,会生成一个唯一的id,并且携带version=1,那么通过数据库的乐观锁操作,如果重复入库的话,会无效,这样就实现了幂等性,避免重复处理相同的数据。

参考

RabbitMQ 应用问题 - 消息幂等性保障(思路)
RabbitMQ系列(四)–消息如何保证可靠性传输以及幂等性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值