Fescar-TM

上一篇 Fescar 分布式事物中间件,学习了fescar 的前世今生及设计原理,接下来几篇来学习一下Fescar 中的三个非常重要的组件TM、RM、TC。

1、模型

再看下fescar 对 三者的定义吧,一个简单的模型,

  • Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
  • Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
  • Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

在应用中的三者的调用关系,

Architecture

实际上,我个人在学习的过程中以为TM 与RM 看起来应该是具有很大的不一样的,但实际上,他们只是role 的不同,处理的时机不一样而已,

2、原码学习

代码的版本基于的是0.1.2版本,学习过程中发布0.3.0版本,但是0.3.0版本中已经去掉了examples model,在示例

com.alibaba.fescar.tm.dubbo.impl.BusinessServiceImpl

@Override
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
public void purchase(String userId, String commodityCode, int orderCount) {
    
    LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
    storageService.deduct(commodityCode, orderCount);
    orderService.create(userId, commodityCode, orderCount);
    throw new RuntimeException("xxx");
}

类中,可以看到,fescar 给需要使用分布式事物的,提供了一个 GlobalTransactional 的注解,前一篇也学习了,在进行数据操作的时候要进行注册,申请全局锁XID,提交前要获取全局锁,那这些操作也定是在代理中进行处理的,来看看,GlobalTransactional 的处理,

2.1、代理的创建

在 dubbo-business.xml 的文件中并没有看到我们之前使用的 ProxyFactoryBean 或者 再或者某类中AspecJ 风格配置方式,都没有,而是通过fescar-spring model 中

com.alibaba.fescar.spring.annotation.GlobalTransactionScanner

一种自动创建代理的改写,通过配置 GlobalTransactionScanner 注册了applicationId 和 txServiceGroup,另外此类的初始化中,初始化了TMClient 并且注册到fescar-server(TC) 中,GlobalTransactionScanner 继承了 AbstractAutoProxyCreator 复写了 wrapIfNecessary 和 getAdvicesAndAdvisorsForBean 方法,getAdvicesAndAdvisorsForBean 方法中返回了 拦截器   GlobalTransactionalInterceptor ,完成了对添加了 GlobalTransactional 注解的bean 的代理,

2.2、代理的执行

代理创建完成之后,首先会执行方法拦截器 GlobalTransactionalInterceptor  invoke() 方法,

@Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
        final GlobalTransactional anno = getAnnotation(methodInvocation.getMethod());
        if (anno != null) {
            try {
                return transactionalTemplate.execute(new TransactionalExecutor() {
                    @Override
                    public Object execute() throws Throwable {
                        return methodInvocation.proceed();
                    }

                    @Override
                    public int timeout() {
                        return anno.timeoutMills();
                    }

                    @Override
                    public String name() {
                        String name = anno.name();
                        if (!StringUtils.isEmpty(name)) {
                            return name;
                        }
                        return formatMethod(methodInvocation.getMethod());
                    }
                });
            } catch (TransactionalExecutor.ExecutionException e) {
                TransactionalExecutor.Code code = e.getCode();
                switch (code) {
                    case RollbackDone:
                        throw e.getOriginalException();
                    case BeginFailure:
                        failureHandler.onBeginFailure(e.getTransaction(), e.getCause());
                        throw e.getCause();
                    case CommitFailure:
                        failureHandler.onCommitFailure(e.getTransaction(), e.getCause());
                        throw e.getCause();
                    case RollbackFailure:
                        failureHandler.onRollbackFailure(e.getTransaction(), e.getCause());
                        throw e.getCause();
                    default:
                        throw new ShouldNeverHappenException("Unknown TransactionalExecutor.Code: " + code);

                }
            }

        }
        return methodInvocation.proceed();
    }

代理方法会调用 transactionalTemplate execute() 

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;
    }

第一步:申请XID全局事物ID

TransactionalTemplate.java

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



GlobalTransactionContext.java

public static GlobalTransaction getCurrent() {
        GlobalTransaction tx = THREAD_TRANSACTION_CONTEXT.get();
        if (tx != null) {
            return tx;
        }
        String xid = RootContext.getXID();
        if (xid == null) {
            return null;
        }
        tx = new DefaultGlobalTransaction(xid);
        THREAD_TRANSACTION_CONTEXT.set(tx);
        return THREAD_TRANSACTION_CONTEXT.get();
    }

GlobalTransaction 是从ThreadLocal 本地变量中获取的,这里有个点要说下,目前fescar 的注解依赖于spring ,对于spring 事物传播性的支持,

  • PROPAGATION_REQUIRED: 默认支持
  • PROPAGATION_SUPPORTS: 默认支持
  • PROPAGATION_MANDATORY:应用通过 API 来实现
  • PROPAGATION_REQUIRES_NEW:应用通过 API 来实现
  • PROPAGATION_NOT_SUPPORTED:应用通过 API 来实现
  • PROPAGATION_NEVER:应用通过 API 来实现
  • PROPAGATION_NESTED:不支持

再看下spring 几个事物的定义:

PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

 

第二步:开启一个事物

@Override
public void begin(int timeout, String name) throws TransactionException {
   // 如果没有事物,并且当前角色是TM 也就是事物发起者
   if (xid == null && role == GlobalTransactionRole.Launcher) {
       xid = transactionManager.begin(null, null, name, timeout);
       status = GlobalStatus.Begin;
       RootContext.bind(xid);
   } else {
       if (xid == null) {
           throw new ShouldNeverHappenException(role + " is NOT in a global transaction context.");
       }
       LOGGER.info(role + " is already in global transaction " + xid);
   }

}

第三步:业务处理

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

通过回调的形式进行处理

第四步:提交 / 回滚

先看下提交操作吧

TransactionalTemplate.java

// 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);

}


DefaultGlobalTransaction.java

@Override
public void commit() throws TransactionException {
    check();
    RootContext.unbind();
    if (role == GlobalTransactionRole.Participant) {
       // Participant has no responsibility of committing
       return;
    }
    status = transactionManager.commit(xid);

}


DefaultTransactionManager.java

@Override
public GlobalStatus commit(String xid) throws TransactionException {
    long txId = XID.getTransactionId(xid);
    GlobalCommitRequest globalCommit = new GlobalCommitRequest();
    globalCommit.setTransactionId(txId);
    GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit);
    return response.getGlobalStatus();
}

 

再看下回滚操作

TransactionalTemplate.java

// 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);

}



DefaultGlobalTransaction.java

@Override
public void rollback() throws TransactionException {
   check();
   RootContext.unbind();
   if (role == GlobalTransactionRole.Participant) {
      // Participant has no responsibility of committing
      return;
   }
   status = transactionManager.rollback(xid);

}


DefaultTransactionManager.java

@Override
public GlobalStatus rollback(String xid) throws TransactionException {
   long txId = XID.getTransactionId(xid);
   GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
   globalRollback.setTransactionId(txId);
   GlobalRollbackResponse response = (GlobalRollbackResponse) syncCall(globalRollback);
   return response.getGlobalStatus();
}

三个方法依次进行调用,最后会向TC 发送提交或者回滚的指令,然后由TC向业务分支下发回滚的指令,各分支进行回滚操作

ok 以上大概整个TM 的工作流程,总结下,

1、首先项目启动的时候就会想TC(server)进行注册

2、事物启动的时候,申请一个XID或者加入一个全局事物,

3、业务逻辑处理

4、全局事物提交或者失败的处理,并且发送指令给TC

 

参考:https://github.com/alibaba/fescar/wiki/%E6%A6%82%E8%A7%88

 

你可能感兴趣的:(Fescar-TM)