Fescar(Seata)-Springcloud流程分析-1阶段

Fescar是阿里18年开源的分布式事务的框架。Fescar的开源对分布式事务框架领域影响很大。做为开源大户,Fescar来自阿里的GTS,经历了好几回双十一的考验,一经开源便颇受关注。今天就来看了Fescar的代码,看看究竟是怎么一回事。java

 

Fescar与XA两阶段提交

 在XA协议中分为两阶段: git

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

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

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

缺点sql

一、同步阻塞问题。执行过程当中,全部参与节点都是事务阻塞型的。当参与者占有公共资源时,其余第三方节点访问公共资源不得不处于阻塞状态。数据库

二、单点故障。因为协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)api

三、数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求以后,发生了局部网络异常或者在发送commit请求过程当中协调者发生了故障,这回致使只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求以后就会执行commit操做。可是其余部分未接到commit请求的机器则没法执行事务提交。因而整个分布式系统便出现了数据部一致性的现象。缓存

四、二阶段没法解决的问题:协调者再发出commit消息以后宕机,而惟一接收到这条消息的参与者同时也宕机了。那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,没人知道事务是否被已经提交。网络

 

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表,就能够达到咱们想要的目的。

 

Fescar整合springcloud

这里直接下载 fescar-samples 

项目的readme写的很详细

准备工做

执行sql/all_in_one.sql

下载0.4.1版本server

客户端与服务端版本号保持一致

启动fescar server

sh fescar-server.sh 8091 ../data/

启动business、storage、account、order

数据库默认链接127.0.0.1:3306,不一样的注意修改

事务成功 GET http://127.0.0.1:8084/purchase/commit

事务回滚 GET http://127.0.0.1:8084/purchase/rollback

看了下项目的依赖和配置文件,和常规的项目相比,多了下面几个依赖

<dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-fescar</artifactId>
                <version>2.1.0.BUILD-SNAPSHOT</version>
                <exclusions>
                    <exclusion>
                        <groupId>com.alibaba.fescar</groupId>
                        <artifactId>fescar-spring</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fescar</groupId>
                <artifactId>fescar-spring</artifactId>
                <version>0.4.1</version>
            </dependency>

 启动相应的项目,观察相应的数据变化,这个demo算是跑起来

Fescar的实现

Fescar将一个本地事务作为一个分布式事务分支,因此若干个分布在不一样微服务中的本地事务共同组成了一个全局事务,结构以下。

 

事务发起

观察整个demo项目,惟一不一样的就是多了个注解 @GlobalTransactional  因此我先从这个注解入手

在 fescar-spring 包中发现了 com.alibaba.fescar.spring.annotation.GlobalTransactionalInterceptor这个类,这个类实现了org.aopalliance.intercept.MethodInterceptor

已动态代理的方式提供了对GlobalTransactional 注解方法的支持

最终咱们的逻辑会到 com.alibaba.fescar.tm.api.TransactionalTemplate#execute方法中

execute方法是事务一阶段的核心,主要作了如下几件事情

1.构造一个全局事务

2.开始事务

  经过 TmRpcClient 请求TC server端,注册一个全局的事物。获得一个全局事务id:XID,将这个XID设置到上下文中

3.执行原始的业务代码

  在springcloud中咱们用feign去请求其余服务,fescar对feign进行了重写,在org.springframework.cloud.alibaba.fescar.feign.FescarFeignClient中fescar对每一个feign的请求都会作一次判断,若是在全局上下文中含有事务id,feign的请求头会带上XID

4.事务的回滚或者提交

提交和回滚本质上都是构造一个请求,请求远程的TC server端

 

5.清除ThreadLocal中的钩子函数,咱们暂时还没用到。

 

事务参与

参与者在收到feign请求时,首先会被org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptor拦截,拦截器会把请求中的XID设置到本地的全局上下文中

而后参与者就开始执行本地的业务。fesca在这里作了大量的背后工做。为了能在jdbc链接中添加了本身的逻辑,fesca重写了Statment以前的逻辑

 

JdbcTemplate数据源被配置成了Fescar实现DataSourceProxy,进而控制了后续的数据库链接使用的是Fescar提供的ConnectionProxy,Statment使用的是Fescar实现的StatmentProxy,最终Fescar就瓜熟蒂落地实现了在本地事务执行先后增长所须要的逻辑

获取代理数据源

 获取代理链接

获取代理statement

在PreparedStatementProxy中,真正的执行sql的逻辑被放在了ExecuteTemplate中

 根据sqltype,这里有多个executor,感受和mybatis的代码风格有点类似

这里咱们已UpdateExecutor为例

 

executor.execute(args);这行代码入口在com.alibaba.fescar.rm.datasource.exec.BaseTransactionalExecutor#execute

将ConnectionProxy与Xid(事务ID)进行绑定,这样后续判断当前本地事务是否处理全局事务中只须要看ConnectionProxy中Xid是否为空

 

再执行com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor#doExecute

这里会判断是不是自动提交

若是是自动提交,就会先设置为为非自动提交再执行executeAutoCommitFalse

这里 executeAutoCommitFalse方法是整个逻辑的核心

先查询Update前对应行记录的快照beforeImage,再执行Update语句,完成后再查询Update后对应行记录的快照afterImage,最后将beforeImage、afterImage生成UndoLog追加到Connection上下文ConnectionContext中 

若是一切顺利,最终咱们会执行本地事务的提交,即执行com.alibaba.fescar.rm.datasource.ConnectionProxy#commit方法,最终逻辑会到com.alibaba.fescar.rm.datasource.ConnectionProxy#processGlobalTransactionCommit

 

逻辑以下

1.注册分支事务到TC server端

2再将ConnectionContext中的UndoLog写入到undo_log表中

3而后调用targetConnection对本地事务进行commit,将UndoLog与业务SQL一块儿提交

4最后上报分支事务的状态(成功 or 失败),并将ConnectionContext上下文重置

 

事务的参与者便完成了本身的逻辑。二阶段中的一阶段的逻辑即是上述代码,这里再引用一张官方的流程图

相关文章
相关标签/搜索