一.try阶段
入口点PaymentServiceImpl.makePayment方法的@Compensable注解,该注解会被aop拦截。
CompensableTransactionAspect切面拦截进入CompensableTransactionInterceptor的interceptCompensableMethod,此时事务类型ROOT全局事务,执行rootMethodProceed方法,创建全局事务transaction,执行compensableMethodContext.proceed(),此时执行try方法,再次被ResourceCoordinatorAspect切面拦截,由于当前是try阶段,执行enlistParticipant(pjp)。
// 首先要知道,此处compensable注解上没有设置上下文编辑器,则默认为DefaultTransactionContextEditor
if (FactoryBuilder.factoryOf(compensable.transactionContextEditor()).getInstance().get(pjp.getTarget(), method, pjp.getArgs()) == null) {
// 此时set方法相当于空执行,why?
FactoryBuilder.factoryOf(compensable.transactionContextEditor()).getInstance().set(new TransactionContext(xid, TransactionStatus.TRYING.getId()), pjp.getTarget(), ((MethodSignature) pjp.getSignature()).getMethod(), pjp.getArgs());
}
class DefaultTransactionContextEditor implements TransactionContextEditor {
@Override
public TransactionContext get(Object target, Method method, Object[] args) {
int position = getTransactionContextParamPosition(method.getParameterTypes());
// 先分析get方法,因为makePayment方法中没有TransactionContext类型参数,所以position为-1,get方法返回null
if (position >= 0) {
return (TransactionContext) args[position];
}
return null;
}
@Override
public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) {
// 方法中没有TransactionContext类型参数, position为-1
int position = getTransactionContextParamPosition(method.getParameterTypes());
if (position >= 0) {
args[position] = transactionContext;
}
}
public static int getTransactionContextParamPosition(Class<?>[] parameterTypes) {
int position = -1;
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals(org.mengyun.tcctransaction.api.TransactionContext.class)) {
position = i;
break;
}
}
return position;
}
public static TransactionContext getTransactionContextFromArgs(Object[] args) {
TransactionContext transactionContext = null;
for (Object arg : args) {
if (arg != null && org.mengyun.tcctransaction.api.TransactionContext.class.isAssignableFrom(arg.getClass())) {
transactionContext = (org.mengyun.tcctransaction.api.TransactionContext) arg;
}
}
return transactionContext;
}
}
接下来是把xid,confirmInvocation(confirmMakePayment),cancelInvocation(cancelMakePayment)封装成对应的partcipant,加入到当前事务ROOT中。执行pjp.proceed(pjp.getArgs()),此时进入makePayment的try方法中。当调用capitalTradeOrderService的record方法时,由于capitalTradeOrderService通过dubbo接口调用,tcc-transaction为该接口生成了代理对象。
源码见tcc-transaction-dubbo模块
proxy对象(record)
1.首先执行代理对象的record方法,同样会被两个切面拦截,因为生成代理对象的record方法有Compensable注解2.CompensableTransactionAspect切面拦截,直接执行pjp.proceed(),此时是本地代理对象的Normal方法,不是PROVIDER和ROOT类型。
3.ResourceCoordinatorAspect切面拦截执行try阶段逻辑。
由于注解事务上下文类型为DubboTransactionContextEditor,通过dubbo隐式传参设置事务上下文new TransactionContext(xid, TransactionStatus.TRYING.getId()),后续添加事务参与者,执行pjp.proceed(pjp.getArgs())。
dubbo RPC(record)
1.首先被远程服务的CompensableTransactionAspect切面拦截,此时事务类型为PROVIDER,执行providerMethodProceed,当前try阶段,调用transactionManager.propagationNewBegin创建分支事务,事务transaction的xid通过dubbo隐式传参获取,以达到主、分支事务的关联。
2.执行compensableMethodContext.proceed(),即远程record的try方法。
3.被远程服务的ResourceCoordinatorAspect切面拦截,由于try阶段则执行enlistParticipant方法,主要获取分支事务,通过GlobalTransactionId创建xid,隐式传参事务上下文,绑定事务与参与者,调用pjp.proceed(pjp.getArgs()),此时真正执行远程record的try方法逻辑。
redPacketTradeOrderService.record处理流程跟capitalTradeOrderService.record一致,不再赘述。
到此try一阶段结束
二.commit阶段
try阶段执行正常,进行commit提交操作,否则进行rollback操作。commit和rollback执行流程基本一致。
commit提交流程如下
入口点rootMethodProceed方法的transactionManager.commit(asyncConfirm)。
1.首先通过getCurrentTransaction()拿到ROOT主事务,设置状态CONFIRMING,循环事务参与者participant,ROOT主事务包含了三个参与者。confirmMakePayment通过反射直接执行即可。
2.capital record通过terminator.invoker的method.invoke(target, invocationContext.getArgs())反射调用,则还是执行本地代理对象方法,此处调用之前使用了隐式传参。
3.通过AOP切面拦截,进入CompensableTransactionInterceptor,直接执行pjp.proceed()。
4.进入ResourceCoordinatorInterceptor,当前try阶段,break结束,继续执行pjp.proceed(pjp.getArgs())。
5.dubbo远程调用record,此时被远程服务的aop切面拦截, 事务类型PROVIDER,执行providerMethodProceed,当前处于COMFIRMING阶段,执行transactionManager.propagationExistBegin获取分支事务(xid),执行commit方法,同样循环参与者,此时的分支事务只包含一个事务参与者。最终反射调用CapitalTradeOrderServiceImpl的confirmRecord方法进行分支事务的提交。最后从本地线程变量中清除已提交事务。
redpacket record方法与capital完全一致,不再赘述。
最后待分支事务都commit结束,删除ROOT主事务,整个分布式事务执行流程结束。