分布式事务实战(二)--可靠消息的最终一致性方案(消息的一致性问题)

前言

消息发送一致性问题:
在分布式部署环境下,通过网络进行通讯,就会有数据传世的不确定性,也就是CAP中的P【会出现分区容错性的问题】。主动方发送消息到消息中间件以及消息中间件到被动方应用题都会出现网络的问题;

如何保证一致性问题

场景:比如说支付模块,订单完成之后需要通知其他系统的场景。
解决方式1:先处理本地事务;再发送消息
问题:如果本地事务操作成功,但是由于各种异常情况(本身应用故障|消息系统故障|网络故障),消息发不出去,就造成了订单系统与其他三方系统的数据不一致的情况。

解决方式2:先发送消息,再处理本地事务。
问题:这种情况,更糟糕了,消息发出去了,但是本地事务操作失败,也会造成了订单系统与其他三方系统的数据不一致的情况。

解决方式3:
JMS标准中的XA协议是否可以保证发送一致性呢?
JMS的标准API中是有支持XA协议的全局性事务型接口。
问题:引入XA方式的分布式事务,要求所有操作的资源必须支持XA协议,两阶段提交协议本身的成本;持久化成本高,全局锁定,性能低。

解决方式4:
这里写图片描述
1.主动方先把消息发送给消息中间件,消息状态为待确认。
2.消息中间件收到消息后,只做存储,不会投递;
3.消息中间件返回持久化结果(成功/失败)。
如果失败,则结束操作;如果成功,接着执行业务操作。
4.业务操作完成后,需要吧操作结果发送给消息中间件。
5.消息中间件收到业务操作结果后,根据结果进行处理。 如果成功,则把消息状态改为“待发送”;后续进行投递; 如果失败,则删除消息,结束。
5.前面流程ok之后, 就会进行消息投递。

异常情况分析

从发送方的角度来看

异常情况 场景分析(原因分析) 是否一致
预发送失败 消息未持久化;业务操作为存储;可能是因为(发送方应用异常,网络,中间件,消息存储等异常) 一致
预发送消息成功:主动方未收到消息存储结果;消息未持久化;业务未执行 消息未持久化;业务未执行 一致
预发送消息成功:主动方未收到消息存储结果;消息已持久化 ;业务未执行 消息已持久化;业务未执行 不一致
收到消息持久化成功的消息:还没来得及执行业务操作就异常,或者执行失败了 消息已持久化;业务未执行 不一致

从中间件的角度来看

异常情况 场景分析(原因分析) 是否一致
中间件未收到主动方的操作结果1 消息已持久化;业务操作未执行(或者失败回滚) 不一致
中间件未收到主动方的操作结果2 消息已持久化;业务操作成功 不一致
消息中间件收到操作结果,但持久化过程中失败1 消息持久化(待确认,状态应该更新为废弃);业务操作未执行(或者失败回滚) 不一致
消息中间件收到操作结果,但持久化过程中失败2 消息持久化(待确认,状态应该更新为待发送);业务操作成功 不一致

总结发送端的异常场景:

异常情况 是否一致 处理方案
消息未持久化,业务操作未执行 一致 一致
消息已持久化(状态待确认),业务操作未执行 不一致 查询确认业务操作结果,删除消息
消息已持久化(状态待确认),业务已经执行 不一致 查询确认业务操作结果,更新为待发送状态,执行投递

MQ队列消息
模型的特点:
1.消息生产者将消息发送到Queue中,消费者监听queue,并接受消息。
2.消息被确认消费之后,会从queue中删除;
3.queue支持多个消费者消费,但对某一个消息,只会有一个消费者消费成功

生成与消费的流程:
1.生产者生成并发送给MQ(同步或者异步)
2.MQ接收到消息,持久化到消息存储(持久化是可选 )
3.MQ向producer返回消息的接收结果
4.消费者会监听并消费MQ的消息
5.消费者获取消息后,执行业务,对消费成功的消息向MQ发送ACk确认
6.确认后,会从队列中删除。

结论:正常的MQ队列的处理流程无法真正实现消息发送一致性;因为他进入队列中没有等到业务确认(或者生产者没有收到消息的确认收到的消息)就进行投递了。会存在一定的风险。

从消费者的角度来看

异常情况 场景分析(原因分析) 操作
1 消费端收到消息,业务处理后应用(或者网络异常或者超时)异常,消息中间件不知道处理结果 重新投递
2 消费端收到消息,业务处理完成,消息中间件的问题导致收不到ACK应答 重新投递
3 消费端收到消息,业务处理完成,消息中间件收到应答,但持久化异常 重新投递

总体来说,消费过程中肯定会出现重复投递的问题,基本上就是因为没有及时确认ACK,所以消费端一定要在业务层面做好幂等的控制。但是消息重发也是有次数限制,会有down机的风险的,所以会限制次数,人工介入干预;后期定期清理。

总结

基于以上的常规以及异常的流程,我们就可以设计一套一致性的系统。 但目前现有的MQ中间件不支持消息发送的一致性流程(先持久化再被业务确认后然后再发送); 如果直接改造源码,难度比较大,并且后续人员维护也非常繁琐。

至于变通的方案,下一篇会讲到 :分布式事务实战(三)–可靠性消息的最终一致性方案【基于本地消息服务】

猜你喜欢

转载自blog.csdn.net/xuxian6823091/article/details/81147072