seata xid是什么_seata源码阅读笔记

seata源码阅读笔记

本文没有seata的使用方法,怎么使用seata可以参考官方示例,详细的很。

本文基于v0.8.0版本,本文没贴代码。

seata中的三个重要部分:

TC:事务协调器,维护全局事务和分支事务的状态,驱动全局提交或回滚,就是seata的服务端。

TM:事务管理器,开始全局事务,提交或回滚全局事务。

RM:资源管理器,管理正在处理的分支事务的资源,向TC注册并报告分支事务的状态,并驱动分支事务的提交或回滚。

seata的初始化

TC启动

RM发送jdbcUrl、applicationId和transactionServiceGroup三个参数向TC发起注册,建立连接。入口是在DataSourceProxy实例化的时候

TM发送applicationId和transactionServiceGroup两个参数向TC发起注册,建立连接。

TM-处理全局事务

全局事务发起者,就是我们加@GlobalTransactional注解的方法,seata会代理我们的方法,通过以下步骤来完成全局事务。

发送全局事务开始请求到TC,TC返回xid(全局事务唯一id),将xid绑定到当前线程

执行我们的业务逻辑

业务逻辑成功,发送全局事务commit请求到TC,如果失败则重试

业务逻辑异常(可配置回滚规则,即哪些异常回滚),发送全局事务rollback请求到TC,如果失败则重试

清理资源,事务结束

源码阅读入口:io.seata.spring.annotation.GlobalTransactionalInterceptor#invoke

RM-处理分支事务

rm需要代理我们项目中的数据源,这一步需要我们修改自己的代码,如下:

@Configuration

public class DataSourceConfig {

@Bean

@ConfigurationProperties(prefix = "spring.datasource")

public DataSource druidDataSource() {

return new DruidDataSource();

}

/**

* 需要将DataSourceProxy 设置为主数据源,否则事务无法回滚

*/

@Primary

@Bean("dataSource")

public DataSource dataSource(DataSource druidDataSource) {

return new io.seata.rm.datasource.DataSourceProxy(druidDataSource);

}

代理数据源的主要目的是代理数据库连接,这样就可以控制分支事务的commit、rollback。

seate的rm-datasoure模块中有这几个代理类DataSourceProxy、ConnectionProxy、StatementProxy,通过代理jdbc中的这几个类,让我们的sql通过ExecuteTemplate来进行执行,这个类就是我们了解seata怎么控制分支事务的入口。下面看分支事务的处理步骤:

将xid绑定到当前数据库连接(xid怎么来的?在后面)

拿到sql执行前数据库表中的数据记录beforeImage(只是受影响的数据及字段,通过解析我们的sql完成)

执行我们的业务sql

拿到sql执行后数据库表中的数据记录afterImage

用变动数据的主键值作为lockKey,并保存到当前数据库连接中

将beforeImage和afterImage转换成undo log,并保存到当前数据库连接中

向TC发送全局锁请求(带着xid和lockKey),TC返回branchId,表示拿到了全局锁,若不成功则重试(默认30次,每次间隔10ms);这个全局锁的是为了防止多个用户同时对同一数据进行修改,造成后面通过undo_log进行回滚操作时数据出错;这个锁在PhaseTwo完成后,TC会进行释放。

将undoLog保存到数据库的undo_log表中

提交数据库事务

向TC报告当前分支事务commit的结果(失败或成功),若报告不成功则重试(默认5次)

commit成功后,清理当前数据库连接绑定的xid

commit失败后,向上抛出异常,以便让tm知道,进行全局事务的回滚

这是PhaseOne的整个过程,下面看PhaseTwo:

tc收到tm的全局事务提交或回滚请求后,会对这个全局事务中的所有分支事务进行通知,rm在收到通知后,进行回滚或者提交的操作

回滚:取出数据库undo_log进行数据补偿还原,成功后删除undo_log;提交:直接删除undo_log

返回tc处理结果

源码阅读入口: io.seata.rm.datasource.StatementProxy#execute

XID在服务链路中的传递

dubbo

用dubbo的filter实现的,源码:io.seata.integration.dubbo.TransactionPropagationFilter

原理就是:上游在filter中把xid放到RpcContext中,下游再从RpcContext拿到xid。

RestTemplate和Feign

对RestTemplate和Feign的支持不在seata-all中,而是在spring-cloud-alibaba-seata中

源码入口:

com.alibaba.cloud.seata.rest.SeataRestTemplateAutoConfiguration

com.alibaba.cloud.seata.feign.SeataFeignClientAutoConfiguration

com.alibaba.cloud.seata.web.SeataHandlerInterceptorConfiguration

原理就是:上游通过拦截器将xid放到请求的header中,下游通过拦截器从header中拿到xid。

seata还支持很多RPC框架,如sofa-rpc、motan等。我们也可以通过类似的方法使seata支持我们自己的rpc框架。

GlobalLock注解

如果是用 GlobalLock 修饰的本地业务方法,虽然该方法并非某个全局事务下的分支事务,但是它对数据资源的操作也需要先查询全局锁,如果存在其他 Seata 全局事务正在修改,则该方法也需等待。所以,如果想要Seata 全局事务执行期间,数据库不会被其他事务修改,则该方法需要强制添加 GlobalLock 注解,来将其纳入 Seata 分布式事务的管理范围。

功能有点类似于 Spring 的 @Transactional 注解,如果你希望开启事务,那么必须添加该注解,如果你没有添加那么事务功能自然不生效,业务可能出 BUG;Seata 也一样,如果你希望某个不在全局事务下的 SQL 操作不影响 AT 分布式事务,那么必须添加 GlobalLock 注解。

TC的高可用设计

seata服务端支持zk、nacos、eureka等作为服务发现,通过数据库实现数据共享,全局事务Session信息、分支事务Session信息,全局锁信息都是放在数据库中

TCC模式

TCC和AT的不同主要体现在RM这边,TC和TM都是一样的。

TCC的RM不会代理我们的数据源了,而是由我们自己指定rollback和commit逻辑,在二阶段中,TM在发起全局事务提交回滚时,RM只需要执行我们指定的rollback和commit方法就行了。

这种模式就是我们业务代码的变动要多些,效率是要比AT模式高些。

RM一阶段源码入口:io.seata.spring.tcc.TccActionInterceptor#invoke

RM二阶段源码入口:io.seata.rm.tcc.TCCResourceManager#branchCommit#branchRollback

参考:

你可能感兴趣的:(seata,xid是什么)