fescar源码分析4-TM

一、概要说明

这里重点分析下TM的实现。下面是TM的定义:

Transaction Manager(TM): 明确全局事务的范围:开始一个全局事务,提交或者回滚一个全局事务

简单来说,就是分布式事务的业务发起方。

二、配置

  1. 定义GlobalTransactionScanner。

        
        
    

构造函数如下。applicationId和txServiceGroup我们这里可以理解为一个分布式事务的唯一标识。可以用于保存一个事务的状态的时候起作用。默认的模式为AT模式。

/**
     * Instantiates a new Global transaction scanner.
     *
     * @param applicationId  the application id
     * @param txServiceGroup the default server group
     */
    public GlobalTransactionScanner(String applicationId, String txServiceGroup) {
        this(applicationId, txServiceGroup, DEFAULT_MODE);
    }
  1. 业务代码配置。
    我们需要关注的是@GlobalTransactional注解,增加了这个注解之后会对这个类的这个方法做扩展(AOP)。
@Override
    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
    public void purchase(String userId, String commodityCode, int orderCount) {
        storageService.deduct(commodityCode, orderCount);
        orderService.create(userId, commodityCode, orderCount);

    }
  1. 跟TC的通讯配置.application.conf
transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
}
service {
  #vgroup->rgroup
  vgroup_mapping.my_test_tx_group = "localRgroup"
  #only support single node
  localRgroup.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
}

三、源码分析

  1. GlobalTransactionScanner
    重点看wrapIfNecessary方法。核心逻辑比较清晰,其实就是读GlobalTransactional的注解,并判断当前的类是否aop的代理类。同时满足这两个条件就增加GlobalTransactionalInterceptor这个拦截器。
LinkedList methodDescList = new LinkedList();
                for (Method method : methods) {
                    GlobalTransactional anno = method.getAnnotation(GlobalTransactional.class);
                    if (anno != null) {
                        methodDescList.add(makeMethodDesc(anno, method));
                    }
                }
                if (methodDescList.isEmpty()) {
                    return bean;
                }

                interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                if (!AopUtils.isAopProxy(bean)) {
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = getAdvisedSupport(bean);
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    for (Advisor avr : advisor) {
                        advised.addAdvisor(0, avr);
                    }
                }
                return bean;
  1. GlobalTransactionalInterceptor
    这个类细节不讨论,核心逻辑在TransactionalTemplate里面实现
  2. TransactionalTemplate
public Object execute(TransactionalExecutor business) throws TransactionalExecutor.ExecutionException {

        // 1. get or create a transaction
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();

        // 2. begin transaction
        try {
            tx.begin(business.timeout(), business.name());

        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.BeginFailure);

        }

        Object rs = null;
        try {

            // Do Your Business
            rs = business.execute();

        } catch (Throwable ex) {

            // 3. any business exception, rollback.
            try {
                tx.rollback();

                // 3.1 Successfully rolled back
                throw new TransactionalExecutor.ExecutionException(tx, TransactionalExecutor.Code.RollbackDone, ex);

            } catch (TransactionException txe) {
                // 3.2 Failed to rollback
                throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.RollbackFailure, ex);

            }

        }

        // 4. everything is fine, commit.
        try {
            tx.commit();

        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.CommitFailure);

        }
        return rs;
    }

我们关注几个核心的逻辑:

  • tx.begin(business.timeout(), business.name());
    请求TC生成xid,并将xid保存到ThreadLocal。这个xid后续在请求其他子事务的时候会带过去。用于生成branch id。
  • rs = business.execute();
    业务逻辑处理。注意这里是通过异常捕获来控制回滚或者提交的,并不是直接通过返回结果判断。
  • tx.commit();
    事务提交。这里对应2PC的第二阶段提交。第一阶段提交是由分支事务直接跟TC交互的。到TM交互就已经是第二阶段的提交了。
  • tx.rollback();
    事务回滚。
  1. xid在TM和RM之间的传递
    上面已经介绍了TM和TC的xid的生成过程。那么xid是如何在TM和RM,以及RM和RM之间传递的呢?
    其实很简单,在dubbo基础上增加一个TransactionPropagationFilter来实现。
public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
        String xid = RootContext.getXID();
        String rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("xid in RootContext[" + xid + "] xid in RpcContext[" + rpcXid + "]");
        }
        boolean bind = false;
        if (xid != null) {
            RpcContext.getContext().setAttachment(RootContext.KEY_XID, xid);
        } else {
            if (rpcXid != null) {
                RootContext.bind(rpcXid);
                bind = true;
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("bind[" + rpcXid + "] to RootContext");
                }
            }
        }

RootContext获取xid成功,则会在请求协议的时候带上TX_XID这个属性。否则会读取请求体中的TX_XID写入RootContext。最终会让xid这个属性在请求的过程中一层层往下传递。

四、问题

  1. TC的commit和rollback分别是执行了什么操作?commit正常的场景应该只需要改变事务的状态即可。但是这里也存在一些问题,因为TM并不知道各个RM的提交状态(TC才知道)。假如这时候一部分子事务提交成功了。一部分子事务提交失败了,这时候TC应该如何处理。
    同理,回滚的场景会更加的复杂。

你可能感兴趣的:(fescar源码分析4-TM)