以AT为例,我们使用Seata时只需要在全局事务的方法上加上@GlobalTransactional,就开启了全局事务的支持。那么Seata的底层到底是怎么实现的呢?

首先我们知道,Seata也是一个SpringBoot项目,如果对Seata源码无从下手,那么不妨从Spring切入:

GlobalTransactionScanner

在Seata中有一个重要的bean组件:GlobalTranscationScanner:

全局事务扫描器,它继承了AbstractAutoProxyCreator, InitializingBean, ApplicationContextAware, DisposableBean接口。这四个都是spring的类,所以想要知道这个GlobalTransactionScanner做了什么工作,我们首先得介绍一下spring知识:

  • AbstractAutoProxyCreator: 是spring的AOP的一个核心类。

  • InitializingBean:此接口为Bean提供了初始化方法的方式,只包含afterPropertiesSet方法

  • DisposableBean:这个接口和InitializingBean是一组的.它只包含destroy方法,作用是为Bean生命周期结束前做一些收尾工作。

  • ApplicationContextAware:实现了这个接口的类可以方便地获得ApplicationContext中的Bean。 可以简单理解为它就是一个spring容器

可以看出来,GlobalTransactionScanner继承了AOP,可以对Bean进行增强,同时还相当于一个spring容器。接下我们来看它做了实现这些接口都做了什么事。

继承AbstractAutoProxyCreator

实现AOP后,我们重点来看下被重写的wrapIfNecessary, 它是AOP中核心的方法

1.wrapIfNecessary()

 @Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// do checkers
try {
synchronized (PROXYED_SET) {
if (PROXYED_SET.contains(beanName)) {
return bean;
}
interceptor = null;
//check TCC proxy:检查是否为TCC(这里我们研究AT,就不看TCC了)
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
//...
} else {
// 不是TCC模式:
Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
//判断是否有相关事务的注解,如GlobalTransactional,如果没有就不会代理,直接返回bean
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
//发现存在事务相关注解的bean,则添加拦截器———GlobalTransactionalInterceptor
if (globalTransactionalInterceptor == null) {
globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationCache.addConfigListener(
ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener)globalTransactionalInterceptor);
}
interceptor = globalTransactionalInterceptor;
}
// ...
PROXYED_SET.add(beanName);
return bean;
}
}
}

说明: 此方法对被全局事务注解的方法Bean进行了增强。具体实现是将拦截器织入代理对象。

2.invoke()

globalTransactionalInterceptor实现了MethodInterceptor这个接口,此接口只有一个方法#invoke():

   @Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
// 获取注解标注的执行方法
Class<?> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 获取全局事务GlobalTransactional的元数据
final GlobalTransactional globalTransactionalAnnotation =
getAnnotation(method, targetClass, GlobalTransactional.class);
// 获取全局锁GlobalLock的元数据.全局锁会将本地事务的执行纳入到seata的管理,一起竞争全局锁,
//保证全局事务在执行时,不会收到本地其他事务的影响。(隔离性)
final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (globalTransactionalAnnotation != null) {
//执行全局事务
return handleGlobalTransaction(methodInvocation, transactional);
} else if (globalLockAnnotation != null) {
//执行全局锁
return handleGlobalLock(methodInvocation, globalLockAnnotation);
}
}
}
return methodInvocation.proceed();
}

说明:

3.handleGlobalTransaction()

我们重点关注执行全局事务的方法handleGlobalTransaction().它的作用是获取事务信息并且执行全局事务:

 Object handleGlobalTransaction(final MethodInvocation methodInvocation,
final AspectTransactional aspectTransactional) throws Throwable {
boolean succeed = true;
try {
// 调用execute方法,执行全局事务
return transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
// 获取事务名称
public String name() {
String name = aspectTransactional.getName();
if (!StringUtils.isNullOrEmpty(name)) {
return name;
}
return formatMethod(methodInvocation.getMethod());
}
//获取事务信息,并封装成TransactionInfo对象
@Override
public TransactionInfo getTransactionInfo() {
// reset the value of timeout
int timeout = aspectTransactional.getTimeoutMills();
if (timeout <= 0 || timeout == DEFAULT_GLOBAL_TRANSACTION_TIMEOUT) {
timeout = defaultGlobalTransactionTimeout;
}
TransactionInfo transactionInfo = new TransactionInfo();
transactionInfo.setTimeOut(timeout);
transactionInfo.setName(name());
transactionInfo.setPropagation(aspectTransactional.getPropagation());
transactionInfo.setLockRetryInterval(aspectTransactional.getLockRetryInterval());
transactionInfo.setLockRetryTimes(aspectTransactional.getLockRetryTimes());
Set<RollbackRule> rollbackRules = new LinkedHashSet<>();
for (Class<?> rbRule : aspectTransactional.getRollbackFor()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (String rbRule : aspectTransactional.getRollbackForClassName()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (Class<?> rbRule : aspectTransactional.getNoRollbackFor()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
for (String rbRule : aspectTransactional.getNoRollbackForClassName()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
transactionInfo.setRollbackRules(rollbackRules);
return transactionInfo;
}
});
} catch (TransactionalExecutor.ExecutionException e) {
//...
}
} finally {
//...
}
}

说明:

  1. 这个方法的主要工作是获取事务的名称和信息,并开启全局事务.
  2. 全局事务的开启调用了transactionalTemplate中的execute()方法.下面继续进入execute方法:

4.execute()

 public Object execute(TransactionalExecutor business) throws Throwable {
// 1. Get transactionInfo 获取事务信息
TransactionInfo txInfo = business.getTransactionInfo();
// 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
// 获取当前事务,主要获取XId
GlobalTransaction tx = GlobalTransactionContext.getCurrent(); // 1.2 Handle the transaction propagation. 配置不同事务的传播行为,执行不同逻辑.
Propagation propagation = txInfo.getPropagation();
SuspendedResourcesHolder suspendedResourcesHolder = null;
try {
//Spring事务的7种传播行为
switch (propagation) {
case NOT_SUPPORTED:
// If transaction is existing, suspend it.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
}
// Execute without transaction and return.
return business.execute();
case REQUIRES_NEW:
// If transaction is existing, suspend it, and then begin new transaction.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
// Continue and execute with new transaction
break;
case SUPPORTS:
// If transaction is not existing, execute without transaction.
if (notExistingTransaction(tx)) {
return business.execute();
}
// Continue and execute with new transaction
break;
case REQUIRED:
// If current transaction is existing, execute with current transaction,
// else continue and execute with new transaction.
break;
case NEVER:
// If transaction is existing, throw exception.
if (existingTransaction(tx)) {
throw new TransactionException(
String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
, tx.getXid()));
} else {
// Execute without transaction and return.
return business.execute();
}
case MANDATORY:
// If transaction is not existing, throw exception.
if (notExistingTransaction(tx)) {
throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
// Continue and execute with current transaction.
break;
default:
throw new TransactionException("Not Supported Propagation:" + propagation);
} // 1.3 如果当前事务未空,则新创建一个.
if (tx == null) {
tx = GlobalTransactionContext.createNew();
}
try {
// 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
// else do nothing. Of course, the hooks will still be triggered.
// 开启全局事务.
beginTransaction(txInfo, tx); Object rs;
try {
// Do Your Business
rs = business.execute();
} catch (Throwable ex) {
// 3. The needed business exception to rollback.
// 发生异常,全局回滚.各个数据根据undo_log进行补偿.
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 如果没有异常发生,则提交全局事务
// 4. everything is fine, commit.
commitTransaction(tx); return rs;
} finally {
//5. clear
resumeGlobalLockConfig(previousConfig);
triggerAfterCompletion();
cleanUp();
}
}
}

说明:

  1. 看到这里可能有些似曾相似,这个流程下来不就是AT模式的2个阶段嘛.我们探究到了AT模式的具体实现!

  2. 在此方法中,我们终于看到了开启全局事务的关键方法: beginTransaction(). 不过,我们知道TM要开启全局事务,首先得向TC发起请求. 说明我们还得进入beginTransaction()方法中一探究竟,这里面还调用了不少方法,大家看的时候可以只看注释的一行往下推进:

4.1 beginTransaction()

   private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
try {
triggerBeforeBegin();
//对TC发起请求
tx.begin(txInfo.getTimeOut(), txInfo.getName());
triggerAfterBegin();
} //...
} @Override
public void begin(int timeout, String name) throws TransactionException {
// 判断事务的发起者是不是TM,如果不是抛异常
if (role != GlobalTransactionRole.Launcher) {
assertXIDNotNull();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNull();
String currentXid = RootContext.getXID(); //获取Xid
if (currentXid != null) {
throw new IllegalStateException("Global transaction already exists," +
" can't begin a new global transaction, currentXid = " + currentXid);
}
//调用transactionManager.begin()
xid = transactionManager.begin(null, null, name, timeout);
status = GlobalStatus.Begin;
RootContext.bind(xid);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction [{}]", xid);
}
} @Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
GlobalBeginRequest request = new GlobalBeginRequest();
request.setTransactionName(name);
request.setTimeout(timeout);
// 关键:syncCall 同步请求
GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
if (response.getResultCode() == ResultCode.Failed) {
throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
}
return response.getXid();
} private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
try {
// 通过Netty发起请求
return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
} catch (TimeoutException toe) {
throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
}
}

实现InitializingBean接口

InitializingBean接口只有一个方法afterPropertiesSet(),GlobalTransactionScanner对它进行了重写:

说明: 调用了initCLient方法:初始化了TM和RM

   private void initClient() {
// ....
//init TM
TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
// 日志
//init RM
RMClient.init(applicationId, txServiceGroup);
// 日志
registerSpringShutdownHook(); }

初始化TM:

    public static void init(String applicationId, String transactionServiceGroup, String accessKey, String secretKey) {
// 获取TMRpc客户端实例
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup, accessKey, secretKey);
//初始化 TM Client
tmNettyRemotingClient.init();
}

调用 TmRpcClient.getInstance() 方法会获取一个 TM 客户端实例.

在获取过程中,会创建 Netty 客户端配置文件对象,以及创建 messageExecutor 线程池,该线程池用于在处理各种与服务端的消息交互,在创建 TmRpcClient 实例时,创建 ClientBootstrap,用于管理 Netty 服务的启停,以及 ClientChannelManager,它是专门用于管理 Netty 客户端对象池.

初始化TM客户端:

To Be Continue...

写在最后

博主也是刚开始学习Seata,编程功力不太深,很多代码的精妙之处也看不出来.

参考链接:

Seata AT 模式启动源码分析

视频,本文很多都是从这里整理的

Seata源码分析(一). AT模式底层实现的更多相关文章

  1. netty源码分析(十八)Netty底层架构系统总结与应用实践

    一个EventLoopGroup当中会包含一个或多个EventLoop. 一个EventLoop在它的整个生命周期当中都只会与唯一一个Thread进行绑定. 所有由EventLoop所处理的各种I/O ...

  2. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  3. Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  4. 自定义系统类加载器源码分析与forName方法底层剖析

    基于上一次[https://www.cnblogs.com/webor2006/p/9240898.html]Launcher的分析继续,上次分析到了这: 接着创建应用类加载器,其创建过程其扩展类加载 ...

  5. Seata AT 模式启动源码分析

    从上一篇文章「分布式事务中间件Seata的设计原理」讲了下 Seata AT 模式的一些设计原理,从中也知道了 AT 模式的三个角色(RM.TM.TC),接下来我会更新 Seata 源码分析系列文章. ...

  6. 结合JDK源码看设计模式——原型模式

    定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...

  7. 转:Ogre源码分析之Root类、Facade模式

    Ogre源码分析(一)Root类,Facade模式 Ogre中的Root对象是一个Ogre应用程序的主入口点.因为它是整个Ogre引擎的外观(Façade)类.通过Root对象来开启和停止Ogre是最 ...

  8. 设计模式(十二)——享元模式(Integer缓冲池源码分析)

    1 展示网站项目需求 小型的外包项目,给客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同: 1) 有客户要求以新闻的形式发布 2) 有客户人要 ...

  9. 设计模式(十七)——迭代器模式(ArrayList 集合应用源码分析)

    1 看一个具体的需求 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系.如图: 2 传统的设计方案(类图) 3 传统的方式的问题分析 ...

  10. 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 百篇博客分析OpenHarmony源码 | v36.04

    百篇博客系列篇.本篇为: v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CP ...

随机推荐

  1. MySQL数据库备份--mysqldump用法

    导出要用到MySQL的mysqldump工具,基本用法是:   shell> mysqldump [OPTIONS] database [tables] 如果你不给定任何表,整个数据库将被导出. ...

  2. 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱

    引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...

  3. ES5 Objece.creat实现继承

    Object.create() Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数, ...

  4. Sql Server Analysis Service 转换为UnknownMember的正确设置 (转载)

    转载: http://www.cnblogs.com/OpenCoder/p/4754447.html#commentform 在SSAS中事实表数据被归类到为UnknownMember 的时候分为两 ...

  5. 转: Eclipse 分屏显示同一个文件

    Eclipse 分屏显示同一个文件   场景 : 某个类很大,可能有数千行.当你想要将类开头部分与中间或者靠后的部分进行对比时,请follow如下步骤: Window -> Editor -&g ...

  6. winrt获取文件MD5码

    //小文件 public static string ComputeMD5(byte[] bytes) { var alg = HashAlgorithmProvider.OpenAlgorithm( ...

  7. 【转】深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p

    一.可能的组合: (1)const char*p (2)char const*p (3)char *const p(4)const char **p (5)char const**p (6)char ...

  8. Java的IO操作---File类

    目标 1)掌握File类作用 2)可以使用file类中方法对文件进行读写操作. File类 唯一与文件有关的类.使用file类可进行创建或删除操作,要想使用File类,首先观察File类的构造方法. ...

  9. 把docker当做绿色打包工具

    如题,我只是把docker当成绿色版的各种环境的集成打包工具了 用到了docker的--net=host模式,这个暂时在mac下是有问题的,linux下正常使用,具体docker的网络模式参考这里,此 ...

  10. 《怎样实现通过shell脚本将用户踢出系统》

    下面是一个将用户踢出系统的脚本: #!/bin/bashread -p "input your username " userps aux | grep "^$user& ...