前言
在之前的博客中,已经介绍过了TM在seata AT模式中的处理流程、TC在seata分布式事务中的主要任务以及RM在seata AT模式中的sql语句执行流程,下面我们通过源码讲述分布式事务是如何实现提交或回滚的。
分支事务的提交或回滚
在seata AT模式中,只有当所有的分支事务全部成功提交后,才会触发分布式事务的提交:
public class ConnectionProxy extends AbstractConnectionProxy {
private void processGlobalTransactionCommit() throws SQLException {
try {
// 注册分支事务,添加行锁,其实就是分布式锁
register();
} catch (TransactionException e) {
recognizeLockKeyConflictException(e, context.buildLockKeys());
}
try {
// 插入undolog
UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
// 提交本地事务
targetConnection.commit();
} catch (Throwable ex) {
LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
// 上报分支事务提交失败状态
report(false);
// 抛出异常,最终会被TM捕捉到,触发分布式事务的回滚
throw new SQLException(ex);
}
if (IS_REPORT_SUCCESS_ENABLE) {
// 上报分支事务成功提交状态
report(true);
}
// 恢复现场
context.reset();
}
}
在上述源码分析中,当所有的RM分支事务提交成功后,TC会接收到所有RM分支事务的状态,代码最终会执行到TM的模版逻辑中。
TM触发分布式事务的提交或回滚
TM模版代码:
public Object execute(TransactionalExecutor business) throws Throwable {
// 1. 拿到整理好的@GlobalTransactional注解里面的配置信息
TransactionInfo txInfo = business.getTransactionInfo();
if (txInfo == null) {
throw new ShouldNeverHappenException("transactionInfo does not exist");
}
// 1.1 获取当前的分布式事务,如果为null的话,说明这是分布式事务的发起者;如果不为null,说明这是分布式事务的参与者
GlobalTransaction tx = GlobalTransactionContext.getCurrent();
// 1.2 获取分布式事务的传播级别,其实就是按照spring的传播级别来一套,区别就是spring事务是本地事务,这是分布式事务,原理都一样
Propagation propagation = txInfo.getPropagation();
SuspendedResourcesHolder suspendedResourcesHolder = null;
try {
// 这个switch里面全都是处理分布式事务传播级别的
switch (propagation) {
// 如果不支持分布式事务,如果当前存在事务,那么先挂起当前的分布式事务,再执行业务逻辑
case NOT_SUPPORTED:
// 分布式事务存在,先挂起
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
}
// 执行业务逻辑
return business.execute();
// 如果是每次都要创建一个新的分布式事务,先把当前存在的分布式事务挂起,然后创建一个新分布式事务
case REQUIRES_NEW:
// 如果分布式事务存在,先挂起当前分布式事务,再创建一个新的分布式事务
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
// 之所以用break,是为了后面的代码和其他的传播级别一起共用,业务逻辑肯定还是要执行的
break;
// 如果支持分布式事务,如果当前不存在分布式事务,那么直接执行业务逻辑,否则以分布式事务的方式执行业务逻辑
case SUPPORTS:
// 如果不存在分布式事务,直接执行业务逻辑
if (notExistingTransaction(tx)) {
return business.execute();
}
// 否则,以分布式事务的方式执行业务逻辑
break;
// 如果有分布式事务,就在当前分布式事务下执行业务逻辑,否则创建一个新的分布式事务执行业务逻辑
case REQUIRED:
// If current transaction is existing, execute with current transaction,
// else continue and execute with new transaction.
break;
// 如果不允许有分布式事务,那么一旦发现存在分布式事务,直接抛异常;只有不存在分布式事务的时候才正常执行
case NEVER:
// 存在分布式事务,抛异常
if (existingTransaction(tx)) {
throw new TransactionException(
String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
, tx.getXid()));
} else {
// 不存在分布式事务,执行业务逻辑