Seata(AT模式)源码解析------全局事务的回滚
- [一、TM发起全局事务回滚:TransactionalTemplate#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable)](#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable))
- [二、TC接收全局事务回滚请求:DefaultCoordinator#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext)](#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext))
- [三、TC处理全局事务回滚:DefaultCore#doGlobalRollback(GlobalSession globalSession, boolean retrying)](#doGlobalRollback(GlobalSession globalSession, boolean retrying))
- 四、RM处理分支事务回滚:AbstractRMHandler#handle(BranchRollbackRequest)
如果全局事务的调用链上某个节点发生异常了,会被TM捕获到,然后发起全局事务回滚。
TransactionalTemplate#execute(TransactionalExecutor)
java
try {
// 开启全局事务
beginTransaction(txInfo, tx);
Object rs;
try {
// 执行业务逻辑
rs = business.execute();
} catch (Throwable ex) {
// 回滚全局事务(本篇文章分析的重点)
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 提交全局事务
commitTransaction(tx);
return rs;
} finally {}

一、TM发起全局事务回滚:TransactionalTemplate#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable)
java
private void completeTransactionAfterThrowing(TransactionInfo txInfo, GlobalTransaction tx, Throwable originalException) throws TransactionalExecutor.ExecutionException {
if (txInfo != null && txInfo.rollbackOn(originalException)) {
try {
// 如果异常类型与@GlobalTransaction的rollbackFor属性声明的匹配,才会回滚
rollbackTransaction(tx, originalException);
} catch () {}
} else {
// 异常类型与@GlobalTransaction的rollbackFor属性声明的不匹配,
// 因此不会滚,还是提交
commitTransaction(tx);
}
}
txInfo.rollbackOn(originalException)的作用是判断当前的异常类型是否与@GlobalTransaction注解的rollbackFor属性声明的匹配,这意味着当前异常必须是@GlobalTransaction注解的rollbackFor属性声明的异常类型或子类。
如果异常类型匹配,才会进行全局事务回滚,否则还是进行全局事务提交。

TransactionalTemplate#rollbackTransaction(GlobalTransaction, Throwable)
java
private void rollbackTransaction(GlobalTransaction tx, Throwable originalException) throws TransactionException, TransactionalExecutor.ExecutionException {
// 发起全局事务回滚
tx.rollback();
// 回滚成功后,还要把异常抛出去
throw new ...;
}
DefaultGlobalTransaction#rollback()
java
@Override
public void rollback() throws TransactionException {
// 重试次数
int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
try {
// 在while循环钟进行全局事务回滚,不成功可以重试
while (retry > 0) {
try {
// 发起全局事务回滚
status = transactionManager.rollback(xid);
break;
} catch (Throwable ex) {
retry--;
if (retry == 0) {
throw new TransactionException("Failed to report global rollback", ex);
}
}
}
} finally {}
}
DefaultTransactionManager#rollback(String xid)
java
@Override
public GlobalStatus rollback(String xid) throws TransactionException {
// 创建请求对象
GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
// 设置全局事务id到请求对象中
globalRollback.setXid(xid);
// 调用syncCall方法,通过Netty向TC发起全局事务回滚的请求
GlobalRollbackResponse response = (GlobalRollbackResponse) syncCall(globalRollback);
return response.getGlobalStatus();
}
可以看到最后就是通过Netty向TC发起全局事务回滚的请求,请求对象GlobalRollbackRequest携带了xid。

二、TC接收全局事务回滚请求:DefaultCoordinator#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext)
当TC接收到请求后,会调用DefaultCoordinator的doGlobalRollback方法处理请求。
java
@Override
protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response,
RpcContext rpcContext) throws TransactionException {
// 调用DefaultCore的rollback 方法处理请求
response.setGlobalStatus(core.rollback(request.getXid()));
}
DefaultCoordinator的doGlobalRollback方法调用DefaultCore的rollback方法。
DefaultCore#rollback(String xid)
java
@Override
public GlobalStatus rollback(String xid) throws TransactionException {
// 根据xid查询global_table表,返回GlobalSession对象
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
boolean shouldRollBack = SessionHolder.lockAndExecute(globalSession, () -> {
globalSession.close();
if (globalSession.getStatus() == GlobalStatus.Begin) {
// 修改global_table表对应记录的状态为Rollbacking
globalSession.changeStatus(GlobalStatus.Rollbacking);
return true;
}
return false;
});
// 全局事务回滚处理
doGlobalRollback(globalSession, false);
return globalSession.getStatus();
}
先根据xid查询globa_table表获取到对应的GlobalSession对象,然后修改globa_table对应记录的状态为Rollbacking,最后调用doGlobalRollback进行全局事务回滚处理。

三、TC处理全局事务回滚:DefaultCore#doGlobalRollback(GlobalSession globalSession, boolean retrying)
java
@Override
public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
boolean success = true;
if (globalSession.isSaga()) {
// saga模式的处理逻辑,忽略
} else {
// 获取与当前全局事务关联的分支事务信息
Boolean result = SessionHelper.forEach(globalSession.getReverseSortedBranches(), branchSession -> {
BranchStatus currentBranchStatus = branchSession.getStatus();
if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
// 删除一阶段失败的分支事务
globalSession.removeBranch(branchSession);
return CONTINUE;
}
try {
// 发起rpc远程调用通知RM回滚分支事务
BranchStatus branchStatus = branchRollback(globalSession, branchSession);
switch (branchStatus) {
case PhaseTwo_Rollbacked:
// 分支事务回滚成功,删除branch_table表记录
globalSession.removeBranch(branchSession);
return CONTINUE;
case PhaseTwo_RollbackFailed_Unretryable:
return false;
default:
return false;
}
} catch (Exception ex) {}
});
if (success) {
// 修改global_table表对应记录状态为Rollbacked,然后释放全局锁
SessionHelper.endRollbacked(globalSession);
}
return success;
}
查询与当前全局事务关联的分支事务,然后遍历每一个分支事务,请求对应的RM回滚分支事务,如果回滚成功,就删除branch_table表对应的分支事务记录。
修改global_table表对应记录状态为Rollbacked,然后释放全局锁。

branchRollback(globalSession, branchSession)方法里面会调用到AbstractCore#branchRollback(GlobalSession globalSession, BranchSession branchSession)方法
AbstractCore#branchRollback(GlobalSession globalSession, BranchSession branchSession)
java
@Override
public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
try {
// 请求对象类型为BranchRollbackRequest
BranchRollbackRequest request = new BranchRollbackRequest();
request.setXid(branchSession.getXid()); // 设置全局事务id
request.setBranchId(branchSession.getBranchId()); // 设置分支事务id
request.setResourceId(branchSession.getResourceId());
request.setApplicationData(branchSession.getApplicationData());
request.setBranchType(branchSession.getBranchType());
// 里面就是通过Netty发起rpc远程调用,通知RM进行分支事务回滚
return branchRollbackSend(request, globalSession, branchSession);
} catch () {}
}
最后就是通过Netty发起rpc远程调用,通知RM进行分支事务回滚。

四、RM处理分支事务回滚:AbstractRMHandler#handle(BranchRollbackRequest)
RM接收到TC的分支事务回滚请求时,会调用AbstractRMHandler#handle(BranchRollbackRequest)方法进行处理。
java
@Override
public BranchRollbackResponse handle(BranchRollbackRequest request) {
BranchRollbackResponse response = new BranchRollbackResponse();
exceptionHandleTemplate(new AbstractCallback<BranchRollbackRequest, BranchRollbackResponse>() {
@Override
public void execute(BranchRollbackRequest request, BranchRollbackResponse response)
throws TransactionException {
// 处理分支事务回滚
doBranchRollback(request, response);
}
}, request, response);
return response;
}
AbstractRMHandler#doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)
java
protected void doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)
throws TransactionException {
String xid = request.getXid();
long branchId = request.getBranchId();
String resourceId = request.getResourceId();
String applicationData = request.getApplicationData();
// 处理分支事务回滚
BranchStatus status = getResourceManager().branchRollback(request.getBranchType(), xid, branchId, resourceId,
applicationData);
response.setXid(xid);
response.setBranchId(branchId);
response.setBranchStatus(status);
}
DataSourceManager#branchRollback(...)
java
@Override
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
DataSourceProxy dataSourceProxy = get(resourceId);
try {
// 调用UndoLogManager的undo方法,查询undo_log表对应记录,生成回滚sql并执行
UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).undo(dataSourceProxy, xid, branchId);
} catch () {}
return BranchStatus.PhaseTwo_Rollbacked;
}
最后就是调用UndoLogManager的undo方法,查询undo_log表对应的记录,解析出回滚sql,然后执行回滚sql进行反向补偿。
