seata分布式事务TCC模式的源码分析

今天我们进行seata分布式事务TCC模式的源码分析:

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

重点看一下 RM 那边的源码 RM 是通过 TM 这边的远程接口调用的,调用到了 RM 的代理对象,代理的生成:在 RM 的工程启动的时候,有@LocalTCC @TwoPhaseBusinessAction 注解的类(注解在接口中)就会走上述截图代码生成代理对象,代理对象的 advice 就是 TccActionInterceptor,所以当 TM 调用到远程的 RM 时,最终会调到 TccActionInterceptor 的 invoke 方法,我们只要看看 invoke 方法干了什么即可。
 

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模式的使用,敬请期待!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值