前言:最近,在家里养伤,因为博主骑自行车不当心摔跤了,给本身形成了影响,同时也给公司形成了影响,没有按时报到。但愿你们骑自行车时必定要当心,手里不要拿手机,仍是那句话:道路千万条,安全第一条,行车不规范,亲人两行泪。好了,这是血的教训。今天的主题不是教如何骑自行车,哈哈哈。言归正传,利用在家养伤总结一下面试中常常问到的在微服务架构中如何解决分布式事务的问题。由于,这个问题,当时回答的不是太好,下来也查询不少资料,算是总结一下学习的心得,若是有不对的地方还请大佬们能多多指点。html
首先这个问题是出如今微服务架构、分布式环境中的,在单机系统中是不用考虑这个问题的,首先咱们来看下分布式系统的CAP原则和BASE理论。面试
咱们知道,这个三个特征最多只能知足两个,三者不可兼得。一致性:全部节点在同一时间点全部的数据都是一致的。可用性:在任什么时候候分布式系统老是能够成功读和写。分区容忍性:在某些节点由于网络故障时,仍然可以知足一致性和可用性的服务。数据库
选择CA:放弃p 等同于放弃分布式系统,只存在于单机系统
选择CP:也就是选择分区容忍性和强一致性,容许在极端状况下出现暂时服务的不可用。
选择AP:容许出现数据的短时不一致,在服务注册的场景短时间的数据不一致,不会对服务形成影响。所以采用AP原则的注册中心才是微服务比较合适的选择。安全
而后,介绍一下BASE理论,如图底部的词汇,BASE指Basically Available 基本可用,Soft-state 软状态(状态容许有短期不一样步,异步), Eventual Consistency 最终一致性,它是对CAP中一致性和可用性的权衡的结果,BASE的核心思想是即便没法作到强一致性,也能够根据系统特性,采用适当的方式达到最终一致性。基于这样的理论知识,咱们只要作到最终一致性就能够解决分布式系统中的问题了。在分布式系统中,最重要的是知足业务需求,而不是追求抽象、绝对的系统特性。网络
若是感受仍是不太明白建议参考CAP框架做者的这篇文章:https://www.cnblogs.com/savorboard/p/base-an-acid-alternative.html架构
一图胜千言:框架
解决方案:异步
(1)刚性事务分布式
全局事务(标准的分布式事务)微服务
优势:严格的ACID;
缺点:效率很是低(微服务架构下已经不太合适)
缘由:1)全局事务方式下,全局事务管理器(TM)经过XA接口使用二阶段提交协议(2pc)与资源层(如数据库进行交互)。使用全局事务,数据被lock的时间跨整个事务,直到全局事务结束。
2)2pc是反可伸缩模式,在事务处理过程当中,参与者须要一直持有资源直到整个分布式事务结束,这样当业务规模愈来愈大的状况下,2pc的局限性就愈来愈明显,系统可伸缩性就会变的不好。
3)与本地事务相比,XA协议的系统开销至关大,于是应当谨慎考虑是否确实须要分布式事务。并且只有支持XA协议的资源才能参与分布式事务。
(2)柔性事务
可靠消息最终一致性(异步确保型)
TCC(两阶段型、补偿型)【略】
最大努力通知(非可靠消息、按期校对)【略】
咱们如今知道有这么多解决分布式事务的方案,咱们可否本身去实现一个分布式事务框架呢?假如咱们选择柔性事务中的,可靠消息最终一致(异步确保型)这种方案来实现就不得不涉及到消息中间件的使用了,消息中间价在分布式系统中的主要做用是,异步通信、解耦、削峰填谷等。以下图所示:
若是按照上面的图,你们可能认为它很简单呀,一个发送消息,一个接受消息。可是在分布式系统中,须要经过网络进行通讯的,就引入了数据传输的不肯定性,也就是CAP理论中的P(分区容错性的问题),以下图所示:
很显然,正是由于跨网络,就会产生消息发送不一致的问题,也就是说,若是个人业务操做成功,那么有这个业务操做所产生的消息必定要成功投递出去,不然就丢失消息。那么咱们该怎么解决这样的问题呢?
也就是如何保障消息发送一致性?一般咱们在使用消息队列来处理业务都会遇到这样的场景:
public void CompleteOrder() { //订单处理 orderService.OrderProcess(); //财务处理(发送消息) ...... }
(1)若是业务操做成功,执行消息发送前应用故障,消息发送不去去,致使消息丢失(订单系统和财务系统数据产生不一致)
(2)若是业务操做成功,应用正常,但消息系统故障或者网络故障,也会致使消息发送不出去(订单系统和财务系统数据产生不一致)。
另一种处理方案:
public void CompleteOrder() { //财务处理(发送消息) ...... //订单处理 orderService.OrderProcess(); }
也就是,我先发送消息再处理订单,这种作法更不可控了,消息发送出去了,可是订单处理失败了(致使订单和财务系统数据的不一致),记住在处理这种场景,必定是业务数据先入库,再发送消息的。
貌似这两种方式都不能保证业务数据的一致性,固然,咱们能够借助JMS标准中的XA协议方式来保证消息发送的一致性,可是这种方式引入了XA,违背了柔性事务的初衷,会带来不少局限性:
(1)要求业务操做的资源(也就是数据库)必须支持XA协议(并非全部的数据库都支持XA)
(2)两阶段提交协议的成本
(3)持久化成本等DTP模型的局限性(全局锁、成本高、性能低)
另一种变通的作法以下:(这种方案只是解决的业务处理成功,消息必定能发送到消息中间件中)
问题:
(1)上面的消息发送一致性方案的正向流程是可行的,可是若是遇到异常流程该怎么处理?
(2)消息发送到消息中间件能获得保障,可是消息的准确消费又如何保障呢?
(3)有没有支持这种发送一致性流程的现成消息中间件?
先来聊聊问题(1),有哪些场景会出现异常,以下图所示:
从主动应用方(生产者)来分析:
(1)预发消息失败,消息未进行存储,业务操做未执行(可能的缘由:主动方应用、网络、消息中间件、消息存储等),这种场景不会出现不一致性。
(2)预发送消息后,主动方应用没有收到返回消息存储结果,可能的状态有:消息未进行存储,业务操做未执行(不会出现不一致性);若是消息已进行存储【待确认】,业务操做未执行,会出现不一致性。
(3)收到消息存储成功的返回结果,但未执行业务操做就失败,可能的状态有:消息已进行存储【待确认】,业务操做未执行,会出现不一致性的问题。
从消息中间件的角度分析:
(1)消息中间件没有收到主动方应用的业务操做处理结果,可能的状态:消息已经存储(待确认),业务操做未执行(或者业务操做出错回滚了),会出现数据的不一致性问题;若是消息已经存储(待确认),业务操做成功,也会出现数据的不一致性。
(2)消息中间件收到业务操做结果(成功/失败),但处理消息存储中的消息状态失败,可能的状态:消息已经存储(待确认),业务操做未执行(或业务操做出错回滚了),会出现数据的不一致性问题;若是消息已经存储(待确认),业务操做成功,也会出现数据的不一致性问题。
那咱们该如何处理这样的异常问题呢?以下图所示:
这样就能够处理异常的流程,有人可能会说,异常处理流程也可能发生异常呀,其实你们认真看的话,异常处理基本上都是查询,若是查询出现了异常,定时补救也是能够的。
总结:关于上面异常处理总结
(1)消息未进存储,业务操做未执行,不会出现一致性问题。
(2)消息已进行存储(待确认),业务操做未执行,会出现一致性问题,异常处理手段,确认业务操做结果,处理消息【删除消息】
(3)消息已进行存储(待确认),业务操做成功,会出现一致性问题,异常处理手段,确认业务操做结果,处理消息【更新消息状态,执行消息投递】
对于问题(3)有没有现成的消息中间件支持消息发送一致性,答案是:没有。是由于经常使用的MQ队列消息的处理流程没法实现消息发送一致性,所以直接使用现成的MQ中间件产品没法实现可靠消息最终一致性的分布式解决方案。
对于问题(2)消息发送到消息中间件能获得保障,可是消息的准确消费又如何保障呢?
之前的流程,以下图所示:
红框圈住的就是消息的消费流程,在这个流程中,任何一个环节都有可能出问题具体到下面这张图:
那咱们如何处理,这些异常呢?处理的方法有不少,这里列出常规的作法:对于未确认的消息,采用按照规则从新投递的方式进行处理。这里就不介绍了, 固然,咱们须要处理极端状况:消息重发也得有次数限制,要否则就变成了死循环,对于超太重发次数的消息,进入到死信队列,等待人工干预或者延后按期处理。同时也须要处理被动方在处理业务时要实现幂等操做。
好了,暂时分享到这里吧,虽然在分布式系统中已经有现成的框架,好比.net core 中的 CAP框架,很是的不错,咱们生产环境中已经用了很长时间,框架能帮助咱们解决实际场景下的问题,可是咱们也要明白框架背后的原理,明白大概的原理,再去看CAP的源码,你会恍然大悟的!哈哈哈哈。
参考资料:
龙果学院 吴水成老师 《微服务架构的分布式事务解决方案》 https://www.roncoo.com/details/7ae3d7eddc4742f78b0548aa8bd9ccdb
CAP框架做者 杨老师 https://www.cnblogs.com/savorboard/p/base-an-acid-alternative.html
做者:郭峥
出处:http://www.cnblogs.com/runningsmallguo/
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。