解密分布式事务框架-Fescar

1.分布式事务

在去年的时候我写过一篇关于分布式事务的文章再有人问你分布式事务,把这篇扔给他。再这篇文章中我叫你们能不用分布式事务就别用分布式事务,由于会引入不少的复杂度。当时说这个的时候其实还有一个缘由,没有大厂的成熟开源解决方案,虽然再网上有不少开源的分布式事务框架,可是都不是太成熟,没有大量的业务验证。它不像其余的分布式中间件有大量的成熟的解决方案,好比分布式消息队列中间件:Apache Kafka,Apache RocketMQ,Apache Pulsar这三个均是Apache顶级项目;又好比分布式任务调度,也有不少的开源好比XXL-JOB,Elastic-Job都是有不少的公司在使用。java

而咱们的分布式事务框架,却一直没有一个通过大量业务验证的框架。经过个人一些了解,其实各大公司都是有本身的分布式事务的解决方案,但不少时候都和业务上强耦合了,不适于作一些通用的框架。可是阿里在今年年初的时候给了你们一个惊喜,Fescar开源!从而让你们再之后选择分布式事务的时候多了一个选择方案,并且是通过成熟业务验证的方案。git

这篇文章我会带你们认识Fescar,以及他的一些设计。后续的文章还会陆续推出Fescar的核心源码解析篇。github

2.初识Fescar

说Fescar以前这里先简单的介绍一下著名的2PC:XA Transactions。 在XA协议中分为两阶段: redis

第一阶段:事务管理器要求每一个涉及到事务的数据库预提交(precommit)此操做,并反映是否能够提交.

第二阶段:事务协调器要求每一个数据库提交数据,或者回滚数据。数据库

优势: 尽可能保证了数据的强一致,实现成本较低,在各大主流数据库都有本身实现,对于MySQL是从5.5开始支持。缓存

缺点:网络

  • 单点问题:事务管理器在整个流程中扮演的角色很关键,若是其宕机,好比在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,致使数据库没法使用。
  • 同步阻塞:在准备就绪以后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
  • 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,好比在第二阶段中,假设协调者发出了事务commit的通知,可是由于网络问题该通知仅被一部分参与者所收到并执行了commit操做,其他的参与者则由于没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
  • 只能用于单一类型数据库,跨数据库种类,其余缓存或者文件等资源不支持。

总的来讲,XA协议比较简单,成本较低,可是其单点问题,以及不能支持高并发(因为同步阻塞)依然是其最大的弱点。并发

能够看见XA协议问题较多因此其做为分布式事务方案讨论的时候基本都会被无情的干掉,可是XA有个很好优势是对业务没有侵入(数据库层面),再业务上不须要编写额外的代码,更加关注业务。框架

在个人那篇文章中我又介绍了几种在应用层面上的分布式事务,可是或多或少的对业务都有必定的入侵。好比咱们的TCC,经常使用的一些TCC框架都须要编写try,confirm,cancel等接口对于现成的业务须要进行转换。若是采用本地消息表模式那么又须要增长额外的表。若是采用事务性消息好比RocketMQ,会让一些没有使用该消息队列的业务须要更换消息队列。若是采用Saga模式,一样咱们须要编写正向和反向接口。能够看见不论采用哪一种分布式事务方案,都会有必定的业务改造,业务入侵成本。异步

而Fescar结合了XA的无侵入的优势和其余应用层事务协议高性能的优势,在应用层实现了二阶段协议的事务,同时对业务代码基本无侵入。

2.1 Fescar和XA

Fescar虽然是二阶段提交协议的分布式事务,可是其解决了上面XA的一些缺点:

  • 单点问题:虽然目前Fescar(0.4.1)仍是单server的,可是Fescar官方预计将会在0.5.x中推出HA-Cluster,到时候就能够解决单点问题。
  • 同步阻塞:Fescar的二阶段,其再第一阶段的时候本地事务就已经提交释放资源了,不会像XA会再两个prepare和commit阶段资源都锁住,而且Fescar,commit是异步操做,也是提高性能的一大关键。
  • 数据不一致:若是出现部分commit失败,那么fescar-server会根据当前的事务模式和分支事务的返回状态的结果来进行不一样的重试策略。而且fescar的本地事务会在一阶段的时候进行提交,其实单看数据库来讲在commit的时候数据库已是一致的了。
  • 只能用于单一数据库: Fescar提供了两种模式,AT和MT。在AT模式下事务资源能够是任何支持ACID的数据库,在MT模式下事务资源没有限制,能够是缓存,能够是文件,能够是其余的等等。固然这两个模式也能够混用。

同时Fescar也保留了接近0业务入侵的优势,只须要简单的配置Fescar的数据代理和加个注解,加一个Undolog表,就能够达到咱们想要的目的。

3.Fescar整体设计

Fescar的设计核心就是他的角色分类。不管是数据库上的XA仍是Fescar都有两个角色TM(事务管理器)和RM(资源管理器),同时Fescar还有一个TC(事务协调器)。咱们先来看看若是没有TC,只有TM和RM会发生什么呢?这里我举个简单的例子,小明去网站上面购买了一个商品,以下图所示:

这里小明其实就是TM(事务管理器),而商品和帐户其实就是咱们的RM(资源管理器),正常状况下可能没问什么问题,帐户和库存都能扣减成功。若是小明再扣减库存的时候成功可是在扣减帐户的时候失败,这个时候就须要对咱们的库存资源进行回滚。小明这个时候就会通知库存把上个阶段扣减的货物补回来。可是回滚库存的时候库存服务不稳定,此次回滚就失败了。通常来讲小明会不断的去重试,直到成功。这样就有个问题小明就一直被阻塞,不能作任何事。这个也能够看作二阶段commit/rollback的时候一直会阻塞TM,网易DDB的XA协议针对这种状况会作一个异步线程的操做。可是在Fescar中一切都是由TC去作的,固然TC其实不只仅会作二阶段失败的重试,他会作二阶段的全部RM的commit和rollback,让咱们的TM作更少的事。

再Fescar中TM,RM,TC的关系以下面官方提供的图:

  • TM:事务的发起者。用来告诉TC,全局事务的开始,提交,回滚。
  • RM:具体的事务资源,每个RM都会做为一个分支事务注册在TC。
  • TC:事务的协调者。也能够看作是Fescar-servr,用于接收咱们的事务的注册,提交和回滚。

这三个角色的分工明确,正是咱们Fescar真正的核心所在,下面我会经过如何使用Fescar带你们更加深入的理解这三个角色。

4.快速开始Fescar

在Fescar的github上已经提供了一个简单的例子https://github.com/fescar-group/fescar-samples,这里咱们须要将这个例子下载下来。

4.1搭建TC Fescar-server

首先咱们搭建事务协调器,也能够叫作搭建Fescar-Server.官网的例子是使用Nacos加上fescar-server已经打好的Jar包运行的。这里为了方便咱们直接下载fescar的代码https://github.com/alibaba/fescar。

找到咱们的Server:

直接运行main方法,该方法会帮助咱们在本地启动一个端口号为8091的fescar-server服务。若是咱们想要进行服务注册,咱们能够修改registry.conf下面的type

能够看见在0.4.1版本的时候支持四种服务注册,nacos,eureka,redis,zk。目前使用redis进行服务注册是有问题的,我也提了一个PR给官方进行修正。固然为了方便其实选择file,后续咱们直连是最为便捷的。

再运行main方法以后,若是出现Server started日志,就表明咱们的TC(事务协调器)成功启动。

4.2 认识TM

上面事务协调器已经搭建完成,咱们接下来须要作的就是将TM和RM运行起来,将对应的操做交给咱们的事务协调器去作。这个时候咱们须要打开fescar-samples这个项目:因为这个项目使用的RPC是Dubbo,他默认配的服务注册中心是Nacos,须要咱们再本地安装一个Nacos,具体安装能够自行搜索,这里不展开讲了。

这里官方例子中,业务关系以下图:

能够看见Business也就是咱们的TM,找到对应的代码:

从代码中咱们知道启动一个分布式事务是须要添加@GlobalTransactional注解的,固然Fescar也提供了API的方式让咱们达到一样的效果。咱们同时也须要修改registry.conf中的Type为file。

若是不是在例子中,而是在咱们真正的业务上彻底不须要修改业务代码,直接在咱们分布式事务发起方添加上这个注解便可。

这个GloablTranscational注解到底作了什么呢?其实加了这个注解的都会走一个叫GlobalTransactionalInterceptor的切面,再这个切面中又会进入TrascationTemplate这个类中的excute方法,这个也是TM的核心方法:

上面的代码有部分删减,只选取了核心的流程。TrascationTemplate其实也是Fescar提供给咱们的API,若是不使用注解那么咱们也能够模仿他的方式去作。能够看见主要分为五步:

  1. 获取当前上下文中是否已经有事务,这一步是经过ThreadLocal去实现的,若是有则获取当前的,若是没有则获取默认的。
  2. 开启事务,这一步是向TC(事务协调器)发出一个请求,注册一个GloabTranscation,这里的timeout是指超过这段时间没有rollback或者commit,TC会帮助咱们作rollback。
  3. 作业务。
  4. 若是该方法抛出了异常,则回滚。这里要注意的时候抛出异常,不少时候咱们会把异常给捕获,致使咱们这里根本没有异常抛出,因此就不会出现回滚。这里的回滚也是向TC发起一个回滚请求,由他帮助咱们对RM进行回滚。
  5. 若是没有异常则向TC发起commit,TC会帮助咱们向RM异步发起提交请求。

TM的核心过程主要是这5步,其余详细的讲解会在后续的代码中体现。

4.3 认识RM

当咱们上面的Business发起业务请求以后,就来到了咱们RM的流程,咱们的Storage和Order服务是怎么知道如今已是处于分布式事务当中了呢?这个就须要借助RPC框架来完成了,这里咱们使用的是Dubbo,fescar为dubbo提供了一个filter,以下图所示:

这里会从rpcContext中获取咱们的xid,也就是咱们的分布式事务ID,若是有的话就证实本次请求处于分布式事务中,那么就会把XID种入咱们的RootContext(fescar的本地上下文)。若是你不是Dubbo,那么也能够根据此方法适配你的RPC。

在RM中咱们应该作什么呢?只须要作下面两步:

  1. 将数据源换成Fescar代理
  2. 在当前数据库中添加一个Undolog的表,用于记录日志回滚。

再Fescar中不只仅是对dataSource进行代理,也会对connection和statement进行代理,以下图:

你们都知道咱们的SQL的具体执行须要依赖Statement,在Fescar的StatementProxy中有以下代码:

能够看见运行的方法是ExecuteTemplate.execute,在execute方法中会根据咱们执行语句的类型记录咱们的Undolog,具体的执行流程参考下面官方的一张图片:

总的来讲咱们的RM核心流程主要有两个:一个是如何识别分布式事务,另一个是经过咱们数据源代理让咱们本来简单的执行SQL流程作了更多的事。

5.总结

写这篇文章的目的,不只仅是让你们知道Fescar,也是让更多的人知道一个优秀分布式事务框架到底应该怎样去作。目前Fescar的版本号是0.4.1,还有不少功能好比HA-Cluster,SpringCloud集成尚未发布。因此目前再线上使用的话可能会遇到Fescar单点的问题,因此目前还不是太推荐线上使用。Fescar的目前规划会在0.5.x版本推出HA-Cluster,到时候其单点问题就会被解决。

这篇文章的原理目前介绍的比较粗浅,后面会陆续推出三篇文章详细介绍分析:TC,TM,RM,敬请期待。

最后这篇文章被我收录于JGrowing-Java分布式事务篇,一个全面,优秀,由社区一块儿共建的Java学习路线,若是您想参与开源项目的维护,能够一块儿共建,github地址为:github.com/javagrowing… 麻烦给个小星星哟。

若是你们以为这篇文章对你有帮助,你的关注和转发是对我最大的支持,O(∩_∩)O:

参考文章

阿里开源分布式事务解决方案Fescar: mp.weixin.qq.com/s/TFGRcHV6E…

相关文章
相关标签/搜索