今天我们进行seata分布式事务TCC模式的源码分析:
首先其TCC源码和AT模式的源码基本都在一起,只是稍微有一些不同的地方,因此我们碰到以前详细分析过的地方就简单提一下,且TCC 的 TM 的源码跟 AT 是一样的,这里就不做分析了,下面我们就开始吧!
1、于是从这里开始:

2、根据策略模式判断是否TCC: TCCBeanParserUtils.isTccAutoProxy 方法进入
public static boolean isTccAutoProxy(Object bean, String beanName, ApplicationContext applicationContext) {
boolean isRemotingBean = parserRemotingServiceInfo(bean, beanName);
//get RemotingBean description
RemotingDesc remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName);
//is remoting bean
if (isRemotingBean) {
if (remotingDesc != null && remotingDesc.getProtocol() == Protocols.IN_JVM) {
//LocalTCC
return isTccProxyTargetBean(remotingDesc);
} else {
// sofa:reference / dubbo:reference, factory bean
return false;
}
} else {
if (remotingDesc == null) {
//check FactoryBean
if (isRemotingFactoryBean(bean, beanName, applicationContext)) {
remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName);
return isTccProxyTargetBean(remotingDesc);
} else {
return false;
}
} else {
return isTccProxyTargetBean(remotingDesc);
}
}
}
3、进入 parserRemotingServiceInfo(bean, beanName);方法:
protected static boolean parserRemotingServiceInfo(Object bean, String beanName) {
RemotingParser remotingParser = DefaultRemotingParser.get().isRemoting(bean, beanName);
if (remotingParser != null) {
return DefaultRemotingParser.get().parserRemotingServiceInfo(bean, beanName, remotingParser) != null;
}
return false;
}
4、进入 parserRemotingServiceInfo 方法:
public RemotingDesc parserRemotingServiceInfo(Object bean, String beanName, RemotingParser remotingParser) {
RemotingDesc remotingBeanDesc = remotingParser.getServiceDesc(bean, beanName);
if (remotingBeanDesc == null) {
return null;
}
remotingServiceMap.put(beanName, remotingBeanDesc);
Class<?> interfaceClass = remotingBeanDesc.getInterfaceClass();
Method[] methods = interfaceClass.getMethods();
if (remotingParser.isService(bean, beanName)) {
try {
//service bean, registry resource
Object targetBean = remotingBeanDesc.getTargetBean();
for (Method m : methods) {
TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class);
if (twoPhaseBusinessAction != null) {
TCCResource tccResource = new TCCResource();
tccResource.setActionName(twoPhaseBusinessAction.name());
tccResource.setTargetBean(targetBean);
tccResource.setPrepareMethod(m);
tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod());
tccResource.setCommitMethod(ReflectionUtil
.getMethod(interfaceClass, twoPhaseBusinessAction.commitMethod(),
new Class[] {BusinessActionContext.class}));
tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod());
tccResource.setRollbackMethod(ReflectionUtil
.getMethod(interfaceClass, twoPhaseBusinessAction.rollbackMethod(),
new Class[] {BusinessActionContext.class}));
//registry tcc resource
DefaultResourceManager.get().registerResource(tccResource);
}
}
} catch (Throwable t) {
throw new FrameworkException(t, "parser remoting service error");
}
}
if (remotingParser.isReference(bean, beanName)) {
//reference bean, TCC proxy
remotingBeanDesc.setReference(true);
}
return remotingBeanDesc;
}
5、进入 remotingParser.getServiceDesc(bean, beanName);方法:

6、进入LocalTCCRemotingParser 解析器类:
@Override
public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
if (!this.isRemoting(bean, beanName)) {
return null;
}
RemotingDesc remotingDesc = new RemotingDesc();
remotingDesc.setReference(true);
remotingDesc.setProtocol(Protocols.IN_JVM);
Class<?> classType = bean.getClass();
Set<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(classType);
for (Class<?> interClass : interfaceClasses) {
if (interClass.isAnnotationPresent(LocalTCC.class)) {
remotingDesc.setInterfaceClassName(interClass.getName());
remotingDesc.setInterfaceClass(interClass);
remotingDesc.setTargetBean(bean);
return remotingDesc;
}
}
throw new FrameworkException("Couldn't parser any Remoting info");
}
7、进入isService(bean, beanName) 方法:
8、进入 LocalTCCRemotingParser 解析器类:
@Override
public boolean isService(Object bean, String beanName) {
Class<?> classType = bean.getClass();
Set<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(classType);
for (Class<?> interClass : interfaceClasses) {
if (interClass.isAnnotationPresent(LocalTCC.class)) {
return true;
}
}
return false;
}
9、进入 isTccProxyTargetBean(remotingDesc);方法:
protected static boolean isTccProxyTargetBean(RemotingDesc remotingDesc) {
if (remotingDesc == null) {
return false;
}
//check if it is TCC bean
boolean isTccClazz = false;
Class<?> tccInterfaceClazz = remotingDesc.getInterfaceClass();
Method[] methods = tccInterfaceClazz.getMethods();
TwoPhaseBusinessAction twoPhaseBusinessAction;
for (Method method : methods) {
twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
if (twoPhaseBusinessAction != null) {
isTccClazz = true;
break;
}
}
if (!isTccClazz) {
return false;
}
short protocols = remotingDesc.getProtocol();
//LocalTCC
if (Protocols.IN_JVM == protocols) {
//in jvm TCC bean , AOP
return true;
}
// sofa:reference / dubbo:reference, AOP
return remotingDesc.isReference();
}
10、点击 TccActionInterceptor 类:
public TccActionInterceptor(RemotingDesc remotingDesc) {
this.remotingDesc = remotingDesc;
}
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
if (!RootContext.inGlobalTransaction()) {
//not in transaction
return invocation.proceed();
}
Method method = getActionInterfaceMethod(invocation);
TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
//try method
if (businessAction != null) {
//save the xid
String xid = RootContext.getXID();
//clear the context
RootContext.unbind();
RootContext.bindInterceptorType(xid, BranchType.TCC);
try {
Object[] methodArgs = invocation.getArguments();
//Handler the TCC Aspect
//跟 TC 通讯,注册 RM,把二阶段的 commit 和 rollback 方法名称传给 TC
Map<String, Object> ret = actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction,
invocation::proceed);//火炬传递,调用被代理方法,执行业务逻辑
//return the final result
return ret.get(Constants.TCC_METHOD_RESULT);
} finally {
//recovery the context
RootContext.unbindInterceptorType();
RootContext.bind(xid);
}
}
return invocation.proceed();
}
11、点击 actionInterceptorHandler.proceed方法:
public Map<String, Object> proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
Callback<Object> targetCallback) throws Throwable {
Map<String, Object> ret = new HashMap<>(4);
//TCC name
String actionName = businessAction.name();
BusinessActionContext actionContext = new BusinessActionContext();
actionContext.setXid(xid);
//set action anme
actionContext.setActionName(actionName);
//Creating Branch Record 远程调用通知TC
String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
actionContext.setBranchId(branchId);
//set the parameter whose type is BusinessActionContext
Class<?>[] types = method.getParameterTypes();
int argIndex = 0;
for (Class<?> cls : types) {
if (cls.getName().equals(BusinessActionContext.class.getName())) {
arguments[argIndex] = actionContext;
break;
}
argIndex++;
}
//the final parameters of the try method
ret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);
//the final result
ret.put(Constants.TCC_METHOD_RESULT, targetCallback.execute());
return ret;
}
12、点击 doTccActionLogStore 方法:
protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction,
BusinessActionContext actionContext) {
String actionName = actionContext.getActionName();
String xid = actionContext.getXid();
//核心
Map<String, Object> context = fetchActionRequestContext(method, arguments);
context.put(Constants.ACTION_START_TIME, System.currentTimeMillis());
//init business context 核心
initBusinessContext(context, method, businessAction);
//Init running environment context
initFrameworkContext(context);
actionContext.setActionContext(context);
//init applicationData
Map<String, Object> applicationContext = new HashMap<>(4);
applicationContext.put(Constants.TCC_ACTION_CONTEXT, context);
String applicationContextStr = JSON.toJSONString(applicationContext);
try {
//registry branch record 和TC通信
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid,
applicationContextStr, null);
return String.valueOf(branchId);
} catch (Throwable t) {
String msg = String.format("TCC branch Register error, xid: %s", xid);
LOGGER.error(msg, t);
throw new FrameworkException(t, msg);
}
}
13、点击fetchActionRequestContext方法:
protected Map<String, Object> fetchActionRequestContext(Method method, Object[] arguments) {
Map<String, Object> context = new HashMap<>(8);
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
for (int j = 0; j < parameterAnnotations[i].length; j++) {
if (parameterAnnotations[i][j] instanceof BusinessActionContextParameter) {
BusinessActionContextParameter param = (BusinessActionContextParameter)parameterAnnotations[i][j];
if (null == arguments[i]) {
throw new IllegalArgumentException("@BusinessActionContextParameter 's params can not null");
}
Object paramObject = arguments[i];
int index = param.index();
//List, get by index
if (index >= 0) {
@SuppressWarnings("unchecked")
Object targetParam = ((List<Object>)paramObject).get(index);
if (param.isParamInProperty()) {
context.putAll(ActionContextUtil.fetchContextFromObject(targetParam));
} else {
context.put(param.paramName(), targetParam);
}
} else {
if (param.isParamInProperty()) {
context.putAll(ActionContextUtil.fetchContextFromObject(paramObject));
} else {
context.put(param.paramName(), paramObject);
}
}
}
}
}
return context;
}
14、点击 initBusinessContext方法:
protected void initBusinessContext(Map<String, Object> context, Method method,
TwoPhaseBusinessAction businessAction) {
if (method != null) {
//the phase one method name
context.put(Constants.PREPARE_METHOD, method.getName());
}
if (businessAction != null) {
//the phase two method name
context.put(Constants.COMMIT_METHOD, businessAction.commitMethod());
context.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod());
context.put(Constants.ACTION_NAME, businessAction.name());
}
}
15、这是TCC一阶段提交的提交的流程,正式提交和回滚思路和也是和AT模式类似的,大家可以试着查看一下。
这里也可以看到,TCC 模式的 RM 的代理主要就是注册了 RM,封 BusinessActionContext 对象传递给 TC,TC 二阶段回调的时候又把 BusinessActionContext 对象传递回 RM,所以就感觉一阶段和二阶段方法共享了 BusinessActionContext 对象的错 觉。
到此结束,下篇我们分析sage模式的使用,敬请期待!
本文详细介绍了Seata TCC模式的源码分析,从TM调用RM的代理对象开始,探讨了TCCBeanParserUtils如何判断TCC模式,以及TccActionInterceptor在事务处理中的角色。在RM端,通过LocalTCC注解找到目标接口,生成代理并注册资源。在事务执行过程中,TCCActionInterceptor保存全局事务ID,与TC通信注册RM,并在后续阶段调用二阶段方法。整个过程展示了TCC模式如何在分布式事务中协调资源。

2万+

被折叠的 条评论
为什么被折叠?



