上一篇 Fescar 分布式事物中间件,学习了fescar 的前世今生及设计原理,接下来几篇来学习一下Fescar 中的三个非常重要的组件TM、RM、TC。
再看下fescar 对 三者的定义吧,一个简单的模型,
在应用中的三者的调用关系,
实际上,我个人在学习的过程中以为TM 与RM 看起来应该是具有很大的不一样的,但实际上,他们只是role 的不同,处理的时机不一样而已,
代码的版本基于的是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 的处理,
在 dubbo-business.xml 的文件中并没有看到我们之前使用的 ProxyFactoryBean 或者
com.alibaba.fescar.spring.annotation.GlobalTransactionScanner
一种自动创建代理的改写,通过配置 GlobalTransactionScanner 注册了applicationId 和 txServiceGroup,另外此类的初始化中,初始化了TMClient 并且注册到fescar-server(TC) 中,GlobalTransactionScanner 继承了 AbstractAutoProxyCreator 复写了 wrapIfNecessary 和 getAdvicesAndAdvisorsForBean 方法,getAdvicesAndAdvisorsForBean 方法中返回了 拦截器 GlobalTransactionalInterceptor ,完成了对添加了 GlobalTransactional 注解的bean 的代理,
代理创建完成之后,首先会执行方法拦截器 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 事物传播性的支持,
再看下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