阿里开源的分布式事务揭秘:Seata原理及流程剖析

背景


在分布式系统中,分布式事务是一个必须要解决的问题,目前使用较多的是最终一致性方案。今天带来的这篇,就给大家分析一下Seata的源码是如何一步一步实现的。读源码的时候我们需要俯瞰起全貌,不要去扣一个一个的细节,这样我们学习起来会快捷而且有效率,我们学习源码需要掌握的是整体思路和核心点。

一、分布式事务简介


分布式事务有各种实现方案,不过大体可分为两类,一种不需要关注事务分支与全局事务的交互过程。另一种是将逻辑拆分成三个部分准备、提交、回滚,分支事务加入全局事务。这两种在Seata里前者称为AT模式,后者称为MT模式。

二、内容


1.分布式事务数据库操作型
MySQL XA方案 是其中一种,这种的话是直接作用于数据库。
其中RM执行本地事务提交与回滚;TM是分布式事务核心管理。
缺点的话,一是不适用于微服务,二很明显由于每次操作不提交,最后导致数据未提交越来越多时候,性能就不是很好了。那么像Seata这种业务层面的解决就相对而已来说性能强大了很多。

2.Seata分布式事务详解
Seata分布式事务是业务层民的解决方案。
而且只依赖于单台数据的事务能力。
Seata包括三个role:
1> TC 事务协调,负责协调并驱动全局事务的提交与回滚
2> TM 控制全局事务边界,负责开启全局事务,决定全局事务的提交与回滚
3> RM 分支事务,负责分支注册,接收TC指令,驱动分支事务回滚与提交
自己理解后画了个菜鸟图,如下:

 3.XA vs Seata AT
AT不需要XA协议,适配于微服务,XA事务性资源的锁都要保持到第二阶段 完成才释放,AT分本地锁和全局锁,本地锁由本地事务管理,全局锁由全局事务管理,在决议第二阶段全局提交时,全局锁马上可以释放。

相关概念

  • XID:一个全局事务的唯一标识,由ip:port:sequence组成
  • Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
  • Transaction Manager ™: 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
  • Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
     

原理


seata涉及到三个角色之间的交互,本文通过流程图将AT模式下的基本交互流程梳理一下,为我们以后的解析打下基础。
假设有三个微服务,分别是服务A、B、C,其中服务A中调用了服务B和服务C,TM、TC、RM三者之间的交互流程如下图:

 

1、服务A启动时,GlobalTransactionScanner会对有@GlobalTransaction注解的方法进行AOP增强,并生成代理,增强的代码位于GlobalTransactionalInterceptor类中,当调用@GlobalTransaction注解的方法时,增强代码首先向TC注册全局事务,表示全局事务的开始,同时TC生成XID,并返回给TM;
2、服务A中调用服务B时,将XID传递给服务B;
3、服务B得到XID后,访问TC,注册分支事务,并从TC获得分支事务ID,TC根据XID将分支事务与全局事务关联;
4、接下来服务B开始执行SQL语句,在执行前将表中对应的数据保存一份,执行后在保存一份,将这两份记录作为回滚记录写入到数据库中,如果执行过程中没有异常,服务B最后将事务提交,并通知TC分支事务成功,服务B也会清除本地事务数据;
5、服务A访问完服务B后,访问服务C;
6、服务C与TC之间的交互与服务B完全一致;
7、服务B和服务C都成功后,服务A通过TM通知TC全局事务成功,如果失败了,服务A也会通知TC全局事务失败;
8、TC记录了全局事务下的每个分支事务,TC收到全局事务的结果后,如果结果成功,则通知RM成功,RM收到通知后清理之前在数据库中保存的回滚记录,如果失败了,则RM要查询出之前在数据库保存的回滚记录,对之前的SQL操作进行回滚。
因为TM、RM、TC之间的交互都是通过网络完成的,很容易出现网络断开的情况,因此TC提供了四个定时线程池,定时检测系统中是否有超时事务、异步提交事务、回滚重试事务、重试提交事务,如果发现了有这四类事务,则从全局事务中获取所有的分支事务,分别调用各个分支事务完成对应的操作,依次来确保事务的一致性。

需要考虑的问题:
通过上面流程的分析可以发现,每次SQL操作(查询除外)时,都会增加额外了三次数据库操作;每次全局事务和分支事务开启时,都涉及到TM、RM与TC的交互;全局事务期间还要承担数据短时不一致的情况,这些都是我们在使用AT模式需要考虑的情况。

在这里插播一下,现在把架构师必须具备的一些技术总结出来一套思维导图和录制了一些相关视频,分享给大家,供大家参考。感兴趣的铁子们可以后台私信【架构】免费获取高清知识体系思维导图及相关资料,后续也会持续更新Java的其它知识点和面试方法资料等等...感兴趣的铁汁们可以持续关注我

项目依赖


seata使用XID表示一个分布式事务,XID需要在一次分布式事务请求所涉的系统中进行传递,从而向feacar-server发送分支事务的处理情况,以及接收feacar-server的commit、rollback指令。所以在分布式系统中使用seata要解决XID的传递问题。seata目前支持全版本的dubbo,对于spring cloud的分布式项目社区也提供了相应的实现

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-alibaba-fescar</artifactId>
</dependency>



该组件实现了基于RestTemplate、Feign通讯时的XID传递功能,详细说明见集成源码深度剖析:Fescar x Spring Cloud
spring-cloud-alibaba-fescar内已包含了fescar-spring的依赖,所以可以不另外引入,查看完整的pom.xml.

业务逻辑


业务逻辑是经典的下订单、扣余额、减库存流程。根据模块划分为三个独立的服务,且分别连接对应的数据库

  1. 订单:order-server
  2. 账户:account-server
  3. 库存:storage-server

另外还有发起分布式事务的业务系统

  • 业务:business-server

项目结构如下图


配置文件

 


seata的配置文件入口为registry.conf查看代码ConfigurationFactory得知目前还不能指定该配置文件,所以名称只能为registry.conf

private static final String REGISTRY_CONF = "registry.conf";
    
public static final Configuration FILE_INSTANCE = new FileConfiguration(REGISTRY_CONF);


在registry中可以指定具体配置的形式,这里使用默认的file形式。在file.conf中有3部分配置内容

1.transport
transport部分的配置对应NettyServerConfig类,用于定义Netty的相关参与,client与server的通信使用的Netty
2.service

service {
 #vgroup->rgroup
 vgroup_mapping.my_test_tx_group = "default"
 #配置Client连接TC的地址
 default.grouplist = "127.0.0.1:8091"
 #degrade current not support
 enableDegrade = false
 #disable
 是否启用seata的分布式事务
 disableGlobalTransaction = false
}


 

//部分代码
public class GlobalTransactionScanner{
    private final boolean disableGlobalTransaction =
           ConfigurationFactory.getInstance().getBoolean("service.disableGlobalTransaction", false);
    
    public void afterPropertiesSet() {
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            return;
        }
        initClient();
    }
}



3.client

 client {
  #RM接收TC的commit通知后缓冲上限
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
}


 


启动Server


前往https://github.com/seata/seata/releases 下载最新版本的 Fescar Server
解压之后的 bin 目录,执行

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



启动成功输出

2019-04-09 20:27:24.637 INFO [main]c.a.fescar.core.rpc.netty.AbstractRpcRemotingServer.start:152 -Server started ... 


 

启动Client


对于Spring boot项目,启动运行xxxApplication的main方法即可,seata的加载入口类位于GlobalTransactionAutoConfiguration

@Configuration
@EnableConfigurationProperties({FescarProperties.class})
public class GlobalTransactionAutoConfiguration {
    private final ApplicationContext applicationContext;
    private final FescarProperties fescarProperties;

    public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext, FescarProperties fescarProperties) {
        this.applicationContext = applicationContext;
        this.fescarProperties = fescarProperties;
    }

    @Bean
    public GlobalTransactionScanner globalTransactionScanner() {
        String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");
        String txServiceGroup = this.fescarProperties.getTxServiceGroup();
        if (StringUtils.isEmpty(txServiceGroup)) {
            txServiceGroup = applicationName + "-fescar-service-group";
            this.fescarProperties.setTxServiceGroup(txServiceGroup);
        }
        
        return new GlobalTransactionScanner(applicationName, txServiceGroup);
    }
}


可以看到支持一个配置项FescarProperties,用于配置事务分组名称

spring.cloud.alibaba.fescar.tx-service-group=my_test_tx_group



如果不指定则用spring.application.name+ -fescar-service-group生成一个名称,所以不指定spring.application.name启动会报错

@ConfigurationProperties("spring.cloud.alibaba.fescar")
public class FescarProperties {
    private String txServiceGroup;

    public FescarProperties() {
    }

    public String getTxServiceGroup() {
        return this.txServiceGroup;
    }

    public void setTxServiceGroup(String txServiceGroup) {
        this.txServiceGroup = txServiceGroup;
    }
}</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值