Seata(AT模式)源码解析
AT模式流程复习
AT模式一阶段流程:

AT模式二阶段(提交)流程:

AT模式二阶段(回滚)流程:

AT模式原理解析
seata是基于SpringAOP实现的,通过SpringAOP的扩展点扫描@GlobalTransactional注解和@GlobalLock注解,生成代理对象,指定了自定义的代理增强器。

自定义的代理增强器的对于@GlobalTransactional注解的处理逻辑就是开启全局事务、执行目标方法,提交/回滚全局事务。

开启全局事务做的事情就是:
- TM请求TC开启全局事务的接口
- TC生成全局事务信息,存入global_table表
- TC返回全局事务id(xid)给TM
- TM把获取到的xid存入ThreadLocal

开启全局事务成功,接下来就要执行目标方,假设我们的目标方法需要执行本地sql,也需要发起rpc远程调用。

执行sql会进入PreparedStatementProxy,PreparedStatementProxy是由ConnectionProxy生成的,而ConnectionProxy又是由DataSourceProxy生成的。

PreparedStatementProxy获取前置镜像、执行sql、获取后置镜像,然后根据前置镜像和后置镜像生成undolog信息。
然后就要向TC注册分支事务,注册成功则提交本地事务,然后上报分支事务状态。

然后如果当前调用链还有后续需要调用的服务,会在请求头中携带xid,被调用方通过SpringMVC拦截器从请求头中获取xid存入ThreadLocal。然后被调用方执行sql时,也是经历"获取前置镜像、执行sql、获取后置镜像,生成undolog信息,注册分支事务,提交本地事务,上报分支事务状态"这一整套流程。

如果一切顺利,TM就会提交全局事务,然后异步TC通知每个参与该全局事务的RM提交本地事务,RM异步删除本地的undolog表记录。

如果调用链出现异常,TM会catch住然后向TC发起全局事务回滚。TC会通知各RM回滚本地事务,RM会查询当前事务对应的undolog记录,根据undolog生成反向补偿sql并执行。

源码解析
GlobalTransactionScanner
java
public class GlobalTransactionScanner extends AbstractAutoProxyCreator {
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
try {
synchronized () {
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
// TCC模式
} else {
// 检查类上或方法上是否有@GlobalTransactional或@GlobalLock修饰
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
// 没有@GlobalTransactional或@GlobalLock修饰,返回原来的bean
return bean;
}
if (globalTransactionalInterceptor == null) {
// 全局事务拦截器,
globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
}
// 保存GlobalTransactionalInterceptor到成员变量interceptor,
// 生成代理对象时放入AOP拦截器链
interceptor = globalTransactionalInterceptor;
}
if (!AopUtils.isAopProxy(bean)) {
// 进行AOP增强
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {}
return bean;
}
} catch () {}
}
}
GlobalTransactionScanner继承了AbstractAutoProxyCreator,重写了wrapIfNecessary方法。wrapIfNecessary方法会扫描到当前bean的类上或方法上是否有@GlobalTransactional或@GlobalLock注解修饰,如果有@GlobalTransactional或@GlobalLock注解修饰,则会对当前bean做增强处理。如果当前bean已经是一个AOP代理bean,那么往AOP拦截器链中放入指定的拦截器GlobalTransactionalInterceptor;如果当前bean是一个普通bean,那么对其进行AOP代理增强,指定AOP拦截器为GlobalTransactionalInterceptor。

GlobalTransactionalInterceptor
该bean被代理增强后,当我们调用代理bean的方法时,会被GlobalTransactionalInterceptor拦截,进入GlobalTransactionalInterceptor 的处理逻辑。
java
public class GlobalTransactionalInterceptor implements MethodInterceptor {
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
// 1、获取Class对象
// 2、获取Method对象
// 3、从Method对象上获取@GlobalTransactional注解和@globalLockAnnotation注解
if (globalTransactionalAnnotation != null) {
// 方法有@GlobalTransactional注解修饰,执行handleGlobalTransaction方法
return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
} else if (globalLockAnnotation != null) {
// 方法有@globalLock注解修饰,执行handleGlobalLock方法
return handleGlobalLock(methodInvocation, globalLockAnnotation);
}
return methodInvocation.proceed();
}
}
GlobalTransactionalInterceptor会判断不同的注解调用不同的方法进行处理。

GlobalTransactionalInterceptor#handleGlobalTransaction
java
Object handleGlobalTransaction(final MethodInvocation methodInvocation,
final GlobalTransactional globalTrxAnno) throws Throwable {
try {
// @GlobalTransaction注解的处理逻辑
// 调用transactionalTemplate的execute方法
return transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
});
} catch () {} finally {}
}
如果是@GlobalTransaction注解,会调用transactionalTemplate进行处理。

GlobalTransactionalInterceptor#handleGlobalLock
java
Object handleGlobalLock(final MethodInvocation methodInvocation,
final GlobalLock globalLockAnno) throws Throwable {
// @GlobalLock注解的处理逻辑
// 调用globalLockTemplate的execute方法
return globalLockTemplate.execute(new GlobalLockExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
});
}
如果是@GlobalLock注解,会调用globalLockTemplate进行处理。

TransactionalTemplate
java
public class TransactionalTemplate {
public Object execute(TransactionalExecutor business) throws Throwable {
try {
// 请求TC开启全局事务
beginTransaction(txInfo, tx);
Object rs;
try {
// 执行业务逻辑
rs = business.execute();
} catch (Throwable ex) {
// 调用链上有异常发生,请求TC回滚全局事务
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 一切顺利,请求TC提交全局事务
commitTransaction(tx);
return rs;
} finally {}
}
}
TransactionalTemplate的execute方法是对@GlobalTransactional注解的具体处理逻辑。
里面就会一个try-catch-finally代码块:全局事务开启,执行业务逻辑;如果调用链上发生异常,则在catch块中进行全局事务回滚;否则提交全局事务。

GlobalLockTemplate
java
public class GlobalLockTemplate {
public Object execute(GlobalLockExecutor executor) throws Throwable {
boolean alreadyInGlobalLock = RootContext.requireGlobalLock();
if (!alreadyInGlobalLock) {
// 当前线程绑定全局锁标记到ThreadLocal中
RootContext.bindGlobalLockFlag();
}
try {
// 执行业务逻辑
return executor.execute();
} finally {
if (!alreadyInGlobalLock) {
// 当前线程解绑全局锁标记
RootContext.unbindGlobalLockFlag();
}
}
}
}
GlobalLockTemplate调用了RootContext.bindGlobalLockFlag()为当前线程在ThreadLocal中绑定一个全局锁标记,然后就调用业务逻辑,完事后再解绑全局锁标记。
给当前线程绑定全局锁标记后,后续对"select for update"语句的处理,seata检查到当前线程绑定了全局锁标记,会在执行查询sql之后,返回查询结果之前,会请求TC查询是否存在全局锁(也就是全局锁已被别人获取)。如果存在全局锁,则当前本地事务回滚进行重试;全局锁不存在,则返回查询结果(之所以全局锁不存在就可以返回查询结果,是因为"select for update"语句会获取本地锁,那么其他线程就不可能再获取到同一个本地锁,而此时确认了TC上没有全局锁,就可以断定没有其他线程对"select for update"语句查询涉及到的记录进行写操作)。

整体流程

由于"开启全局事务"、"分支事务处理"、"全局事务提交/回滚"等的这些分支每一个展开都描述都需要很长的篇幅,因此就不在本篇文件进行解析了,放到后面的文件进行逐一的分析。