目录
TCC模式
Seata整体架构
事务控制相关概念
空回滚
幂等
防悬挂
GlobalTransactionScanner切面扫描
全局事务处理流程(GlobalTransactionalInterceptor)
全局事务注册
全局事务提交
全局事务回滚
一阶段分支事务Try处理流程(TccActionInterceptor)
分支注册&&上下文参数处理
tcc_fence_log
tcc_fence_log表结构
Try阶段插入tcc_fence_log表
二阶段处理流程
Commit处理流程
Rollback处理流程
TCC幂等&&空回滚&&防悬挂控制总结
整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备3个方法:
在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。
Seata由核心三大模块组成:TM、RM 和 TC,其中TM和RM集成在我们的业务系统作为seata的客户端,而TC作为Seata的server端独立部署。
TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,与客户端TM、RM通信,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器:与服务端TC通信,开启全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC通信以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata中,一次事务的执行流程大体如下:
异常场景的处理:
空回滚就是对于一个分布式事务,在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。
出现原因:
幂等就是对于同一个分布式事务的同一个分支事务,重复去调用该分支事务的第二阶段接口,因此,要求 TCC 的二阶段 Confirm 和 Cancel 接口保证幂等,不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致资损等严重问题。
出现原因:
Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致
出现原因:
本文重点在于源码分析,不过多介绍分布式事务相关原理和概念,下面进入正题
Seata全局事务发起、提交/回滚依赖于SpringAop,GlobalTransactionScanner则是aop的入口类,此类继承于AbstractAutoProxyCreator,重写了wrapIfNecessary方法,懂aop都知道,此方法通过添加interceptor到SpringAop的拦截器链中,生成代理对象来完成代理逻辑的处理。
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// do checkers
if (!doCheckers(bean, beanName)) {
return bean;
}
try {
synchronized (PROXYED_SET) {
if (PROXYED_SET.contains(beanName)) {
return bean;
}
interceptor = null;
// tcc代理bean
// 判断接口上是否有@LocalTCC && 接口方法上有@TwoPhaseBusinessAction 注解
// 同时将@TwoPhaseBusinessAction注解内容解析成tccResource,注册到缓存中,key值为TwoPhaseBusinessAction#name属性。
// 以供全局2阶段从缓存中获取tccResource
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
// // 若开启了tcc 幂等、空回滚、防悬挂控制(TwoPhaseBusinessAction#useTCCFence为true),定时清理客户端的tcc_fence_log表记录
TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
//设置tcc代理拦截器
interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener)interceptor);
} else {
// 非tcc模式bean
Class> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
// 查找类和方法上是否有全局事务@GlobalTransactional注解
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
// 设置全局事务代理拦截器。说明@GlobalTransactional和tcc相关注解不能标准在同一类上
if (globalTransactionalInterceptor == null) {
globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationCache.addConfigListener(
ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener)globalTransactionalInterceptor);
}
interceptor = globalTransactionalInterceptor;
}
LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
// 如果当前bean不是代理对象,调用父类wrapIfNecessary生产代理对象
if (!AopUtils.isAopProxy(bean)) {
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {
AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
// 拦截器转换成Advisor
Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
int pos;
// 按照拦截器的order值,添加拦截器到合适的位置。主要是为了指定该拦截在 spring本地事务拦截器TransactionInterceptor的前面/后面
// 像@GlobalTransactionalInterceptor就指定了必须包裹住spring本地事务
for (Advisor avr : advisor) {
// Find the position based on the advisor's order, and add to advisors by pos
pos = findAddSeataAdvisorPosition(advised, avr);
advised.addAdvisor(pos, avr);
}
}
PROXYED_SET.add(beanName);
return bean;
}
} catch (Exception exx) {
throw new RuntimeException(exx);
}
}
/**
* 重写getAdvicesAndAdvisorsForBean,返回设置好的拦截器
* @param beanClass
* @param beanName
* @param customTargetSource
* @return
* @throws BeansException
*/
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource)
throws BeansException {
// 全局事务拦截器/tcc拦截器
return new Object[]{interceptor};
}
}
通过GlobalTransactionScanner添加了全局事务和tcc分支事务的代理拦截,具体拦截逻辑分别在GlobalTransactionalInterceptor和TccActionInterceptor中。
当代码执行到标注有@GlobalTransactional注解的类或者方法时,进入GlobalTransactionalInterceptor拦截处理。
public class GlobalTransactionalInterceptor implements ConfigurationChangeListener, MethodInterceptor, SeataInterceptor {
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
Class> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 查找类||方法上的全局事务注解
final GlobalTransactional globalTransactionalAnnotation =
getAnnotation(method, targetClass, GlobalTransactional.class);
// 代理方法增加@GlobalLock+@Transactional 或者 @GlobalTransaction 防止脏读
final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (!localDisable) {
if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
AspectTransactional transactional;
if (globalTransactionalAnnotation != null) {
transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(),
globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(),
globalTransactionalAnnotation.noRollbackForClassName(),
globalTransactionalAnnotation.noRollbackFor(),
globalTransactionalAnnotation.noRollbackForClassName(),
globalTransactionalAnnotation.propagation(),
globalTransactionalAnnotation.lockRetryInterval(),
globalTransactionalAnnotation.lockRetryTimes());
} else {
transactional = this.aspectTransactional;
}
//执行全局事务处理逻辑
return handleGlobalTransaction(methodInvocation, transactional);
} else if (globalLockAnnotation != null) {
return handleGlobalLock(methodInvocation, globalLockAnnotation);
}
}
}
return methodInvocation.proceed();
}
Object handleGlobalTransaction(final MethodInvocation methodInvocation,
final AspectTransactional aspectTransactional) throws Throwable {
boolean succeed = true;
try {
return transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
public String name() {
String name = aspectTransactional.getName();
if (!StringUtils.isNullOrEmpty(name)) {
return name;
}
// 默认使用 方法名+参数类型名称
return formatMethod(methodInvocation.getMethod());
}
/**
* 组装事务信息
* @return
*/
@Override
public TransactionInfo getTransactionInfo() {
// reset the value of timeout
int timeout = aspectTransactional.getTimeoutMills();
if (timeout <= 0 || timeout == DEFAULT_GLOBAL_TRANSACTION_TIMEOUT) {
timeout = defaultGlobalTransactionTimeout;
}
TransactionInfo transactionInfo = new TransactionInfo();
transactionInfo.setTimeOut(timeout);
transactionInfo.setName(name());
transactionInfo.setPropagation(aspectTransactional.getPropagation());
transactionInfo.setLockRetryInterval(aspectTransactional.getLockRetryInterval());
transactionInfo.setLockRetryTimes(aspectTransactional.getLockRetryTimes());
Set rollbackRules = new LinkedHashSet<>();
for (Class> rbRule : aspectTransactional.getRollbackFor()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (String rbRule : aspectTransactional.getRollbackForClassName()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (Class> rbRule : aspectTransactional.getNoRollbackFor()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
for (String rbRule : aspectTransactional.getNoRollbackForClassName()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
transactionInfo.setRollbackRules(rollbackRules);
return transactionInfo;
}
});
} catch (TransactionalExecutor.ExecutionException e) {
TransactionalExecutor.Code code = e.getCode();
switch (code) {
case RollbackDone:
throw e.getOriginalException();
case BeginFailure:
succeed = false;
failureHandler.onBeginFailure(e.getTransaction(), e.getCause());
throw e.getCause();
case CommitFailure:
succeed = false;
failureHandler.onCommitFailure(e.getTransaction(), e.getCause());
throw e.getCause();
case RollbackFailure:
failureHandler.onRollbackFailure(e.getTransaction(), e.getOriginalException());
throw e.getOriginalException();
case RollbackRetrying:
failureHandler.onRollbackRetrying(e.getTransaction(), e.getOriginalException());
throw e.getOriginalException();
default:
throw new ShouldNeverHappenException(String.format("Unknown TransactionalExecutor.Code: %s", code));
}
} finally {
if (degradeCheck) {
EVENT_BUS.post(new DegradeCheckEvent(succeed));
}
}
}
}
这里使用transactionalTemplate模板封装全局事务的处理,来看看它的execute方法
public Object execute(TransactionalExecutor business) throws Throwable {
// 1. Get transactionInfo
TransactionInfo txInfo = business.getTransactionInfo();
if (txInfo == null) {
throw new ShouldNeverHappenException("transactionInfo does not exist");
}
// 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
// 这里会获取线程上下文中的全局事务xid,如果xid不为null,说明此xid是通过rpc传递过来的
// 那当前应用就是作为此次全局事务的参与方,创建DefaultGlobalTransaction对象。否则,tx为null
GlobalTransaction tx = GlobalTransactionContext.getCurrent();
// 1.2 Handle the transaction propagation.
// 全局事务的传播行为,默认REQUIRED,即继承事务
Propagation propagation = txInfo.getPropagation();
SuspendedResourcesHolder suspendedResourcesHolder = null;
try {
switch (propagation) {
case NOT_SUPPORTED:
// If transaction is existing, suspend it.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
}
// Execute without transaction and return.
return business.execute();
case REQUIRES_NEW:
// If transaction is existing, suspend it, and then begin new transaction.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
// Continue and execute with new transaction
break;
case SUPPORTS:
// If transaction is not existing, execute without transaction.
if (notExistingTransaction(tx)) {
return business.execute();
}
// Continue and execute with new transaction
break;
case REQUIRED:
// If current transaction is existing, execute with current transaction,
// else continue and execute with new transaction.
break;
case NEVER:
// If transaction is existing, throw exception.
if (existingTransaction(tx)) {
throw new TransactionException(
String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
, tx.getXid()));
} else {
// Execute without transaction and return.
return business.execute();
}
case MANDATORY:
// If transaction is not existing, throw exception.
if (notExistingTransaction(tx)) {
throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
// Continue and execute with current transaction.
break;
default:
throw new TransactionException("Not Supported Propagation:" + propagation);
}
// 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
if (tx == null) {
// xid为null,说明是全局事务的发起方。
tx = GlobalTransactionContext.createNew();
}
// set current tx config to holder
GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);
try {
// 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
// else do nothing. Of course, the hooks will still be triggered.
// 如果是全局事务的发起方,通知TC服务端注册一个全局事务
beginTransaction(txInfo, tx);
Object rs;
try {
// Do Your Business
// 执行业务方法逻辑
rs = business.execute();
} catch (Throwable ex) {
// 3. The needed business exception to rollback.
// 全局事务回滚处理
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 4. everything is fine, commit.
// 全局事务提交
commitTransaction(tx);
return rs;
} finally {
//5. clear
resumeGlobalLockConfig(previousConfig);
triggerAfterCompletion();
cleanUp();
}
} finally {
// If the transaction is suspended, resume it.
if (suspendedResourcesHolder != null) {
tx.resume(suspendedResourcesHolder);
}
}
}
这里的逻辑十分清晰吗,对于全局事务的发起方,一个分三步
public class DefaultGlobalTransaction implements GlobalTransaction {
@Override
public void begin(int timeout, String name) throws TransactionException {
// 非全局事务的发起方,do nothing
if (role != GlobalTransactionRole.Launcher) {
assertXIDNotNull();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNull();
// 作为全局事务的发起方,这个时候全局事务还未注册。RooRootContext上下文中的xid理应为null
String currentXid = RootContext.getXID();
if (currentXid != null) {
throw new IllegalStateException("Global transaction already exists," +
" can't begin a new global transaction, currentXid = " + currentXid);
}
// TM向TC注册全局事务,绑定tc返回的全局事务id
xid = transactionManager.begin(null, null, name, timeout);
status = GlobalStatus.Begin;
RootContext.bind(xid);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction [{}]", xid);
}
}
}
这里的TransactionManager是通过spi加载的,一般的框架中都会有自己的spi机制。
public class TransactionManagerHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionManagerHolder.class);
private static class SingletonHolder {
private static TransactionManager INSTANCE = null;
static {
try {
INSTANCE = EnhancedServiceLoader.load(TransactionManager.class);
LOGGER.info("TransactionManager Singleton {}", INSTANCE);
} catch (Throwable anyEx) {
LOGGER.error("Failed to load TransactionManager Singleton! ", anyEx);
}
}
}
/**
* Get transaction manager.
*
* @return the transaction manager
*/
public static TransactionManager get() {
if (SingletonHolder.INSTANCE == null) {
throw new ShouldNeverHappenException("TransactionManager is NOT ready!");
}
return SingletonHolder.INSTANCE;
}
/**
* Set a TM instance.
*
* @param mock commonly used for test mocking
*/
public static void set(TransactionManager mock) {
SingletonHolder.INSTANCE = mock;
}
private TransactionManagerHolder() {
}
}
可以看到TransactionManager默认为DefaultTransactionManager
public class DefaultTransactionManager implements TransactionManager {
@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
GlobalBeginRequest request = new GlobalBeginRequest();
request.setTransactionName(name);
request.setTimeout(timeout);
// 请求TC开启全局事务
GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
if (response.getResultCode() == ResultCode.Failed) {
throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
}
return response.getXid();
}
private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
try {
// TM通过netty发送全局事务开始请求给TC
return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
} catch (TimeoutException toe) {
throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
}
}
}
public class DefaultGlobalTransaction implements GlobalTransaction {
@SuppressWarnings("lgtm[java/constant-comparison]")
@Override
public void commit() throws TransactionException {
// 全局事务的参与方,do nothing
if (role == GlobalTransactionRole.Participant) {
// Participant has no responsibility of committing
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Commit(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNotNull();
// 重试提交次数,默认5
int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
try {
while (retry > 0) {
try {
retry--;
// TM向TC发送全局事务提交请求,返回全局事务状态
status = transactionManager.commit(xid);
break;
} catch (Throwable ex) {
LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
if (retry == 0) {
throw new TransactionException("Failed to report global commit", ex);
}
}
}
} finally {
// xid解绑
if (xid.equals(RootContext.getXID())) {
suspend();
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("[{}] commit status: {}", xid, status);
}
}
}
public class DefaultTransactionManager implements TransactionManager {
@Override
public GlobalStatus commit(String xid) throws TransactionException {
GlobalCommitRequest globalCommit = new GlobalCommitRequest();
globalCommit.setXid(xid);
// 请求TC提交全局事务
GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit);
return response.getGlobalStatus();
}
private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
try {
// TM通过netty发送全局事务请求给TC
return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
} catch (TimeoutException toe) {
throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
}
}
}
public class DefaultGlobalTransaction implements GlobalTransaction {
@SuppressWarnings("lgtm[java/constant-comparison]")
@Override
public void rollback() throws TransactionException {
// 全局事务的参与方,do nothing
if (role == GlobalTransactionRole.Participant) {
// Participant has no responsibility of rollback
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Rollback(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNotNull();
// 重试回滚次数,默认5
int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
try {
while (retry > 0) {
try {
retry--;
// TM向TC发送全局事务回滚请求,返回全局事务状态
status = transactionManager.rollback(xid);
break;
} catch (Throwable ex) {
LOGGER.error("Failed to report global rollback [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
if (retry == 0) {
throw new TransactionException("Failed to report global rollback", ex);
}
}
}
} finally {
// 解绑xid
if (xid.equals(RootContext.getXID())) {
suspend();
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("[{}] rollback status: {}", xid, status);
}
}
}
public class DefaultTransactionManager implements TransactionManager {
@Override
public GlobalStatus rollback(String xid) throws TransactionException {
GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
globalRollback.setXid(xid);
// 请求TC回滚全局事务
GlobalRollbackResponse response = (GlobalRollbackResponse) syncCall(globalRollback);
return response.getGlobalStatus();
}
private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
try {
// TM通过netty发送全局事务请求给TC
return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
} catch (TimeoutException toe) {
throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
}
}
}
再来看看业务代码中tcc分支事务的流程,即TccActionInterceptor拦截处理
public class TccActionInterceptor implements MethodInterceptor, ConfigurationChangeListener, Ordered {
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// 不处于全局事务||禁用||saga模式,直接执行业务逻辑
if (!RootContext.inGlobalTransaction() || disable || RootContext.inSagaBranch()) {
//not in transaction, or this interceptor is disabled
return invocation.proceed();
}
// 获取方法上的TwoPhaseBusinessAction注解
Method method = getActionInterfaceMethod(invocation);
TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
//try method
// 有businessAction注解执行TCC切面逻辑
if (businessAction != null) {
//save the xid
String xid = RootContext.getXID();
//save the previous branchType
BranchType previousBranchType = RootContext.getBranchType();
//if not TCC, bind TCC branchType
//绑定tcc分支类型
if (BranchType.TCC != previousBranchType) {
RootContext.bindBranchType(BranchType.TCC);
}
try {
//Handler the TCC Aspect, and return the business result
// 重点在这里,ActionInterceptorHandler处理
return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessAction,
invocation::proceed);
} finally {
//if not TCC, unbind branchType
if (BranchType.TCC != previousBranchType) {
RootContext.unbindBranchType();
}
//MDC remove branchId
MDC.remove(RootContext.MDC_KEY_BRANCH_ID);
}
}
//not TCC try method
return invocation.proceed();
}
}
public class ActionInterceptorHandler {
public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
Callback
在分支注册的时候,会将分支上下文中供TCC2阶段使用的业务参数序列化成json字符串一同发送到TC服务端,执行完业务方法后,还需要再请求一次TC更新业务参数,也是整个json字符串传递过去的。而在我们真实的业务场景中,要传递给2阶段执行的参数可能是非常多的,一旦事务分支较多,这会给网络IO带来很大的开销。所以其实这里我们是可以优化的,上下文中的参数信息完全可以保存到客户端的数据库中(存储在tcc_fence_log中就挺合适的),一次数据库操作就可以完整的保存下来。而发送给TC的数据只需要包含基本的分支信息就可以了,也不用再多请求一次TC来更新参数内容。
TCC模式通过客户端的tcc_fence_log表来完成幂等、空回滚、防悬挂的控制。下面就具体来看下TCCFenceHandler的逻辑
CREATE TABLE IF NOT EXISTS `tcc_fence_log`
(
`xid` VARCHAR(128) NOT NULL COMMENT 'global id',
`branch_id` BIGINT NOT NULL COMMENT 'branch id',
`action_name` VARCHAR(64) NOT NULL COMMENT 'action name',
`status` TINYINT NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',
`gmt_create` DATETIME(3) NOT NULL COMMENT 'create time',
`gmt_modified` DATETIME(3) NOT NULL COMMENT 'update time',
PRIMARY KEY (`xid`, `branch_id`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_status` (`status`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
可以看到这个表的核心字段就三个:xid-全局事务id,branch_id-分支事务id,action_name-分支名称,主键为(xid,branch_id)
public class TCCFenceHandler {
public static Object prepareFence(String xid, Long branchId, String actionName, Callback
对于try阶段而已,只需要检查第二阶段是否已经执行完成,如果已完成,则不再执行。这里通过往tcc_fence_log表中insert一条记录来保证此机制,如果出现duplikey异常,说明2阶段cancel比try先执行,2阶段cancel为了支持空回滚,也会往此表中insert一条记录,而2阶段commit则是需要检查表中是否有记录,并更新状态。
首先要明确的是二阶段无论是commit还是rollback都是由TC服务端发起的请求,对于client端来说,一阶段事务结束,业务流程就已经结束了。2阶段处理已经处于处理tc请求的异步线程中了,而非业务线程中。分支提交/回滚请求是由RM来处理的,委托给DefaultRMHandler处理
public class DefaultRMHandler extends AbstractRMHandler {
//通过spi加载所有的RMHandler放到map中,key值为BranchType,value为具体handler对象
protected void initRMHandlers() {
List allRMHandlers = EnhancedServiceLoader.loadAll(AbstractRMHandler.class);
if (CollectionUtils.isNotEmpty(allRMHandlers)) {
for (AbstractRMHandler rmHandler : allRMHandlers) {
allRMHandlersMap.put(rmHandler.getBranchType(), rmHandler);
}
}
}
//处理分支commit请求,对应BranchCommitRequest
//返回分支处理结果
@Override
public BranchCommitResponse handle(BranchCommitRequest request) {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(request.getBranchId()));
//调用具体的handler处理请求
return getRMHandler(request.getBranchType()).handle(request);
}
//处理分支rollback请求,对应BranchRollbackRequest
//返回分支处理结果
@Override
public BranchRollbackResponse handle(BranchRollbackRequest request) {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(request.getBranchId()));
//调用具体的handler处理请求
return getRMHandler(request.getBranchType()).handle(request);
}
// 根据分支类型获取对应的Rmhandler
protected AbstractRMHandler getRMHandler(BranchType branchType) {
return allRMHandlersMap.get(branchType);
}
}
不同的分支类型对应不同的RmHandler
public abstract class AbstractRMHandler extends AbstractExceptionHandler
implements RMInboundHandler, TransactionMessageHandler {
@Override
public BranchCommitResponse handle(BranchCommitRequest request) {
//分支提交响应体
BranchCommitResponse response = new BranchCommitResponse();
//模板方法执行正常/异常情况响应赋值
exceptionHandleTemplate(new AbstractCallback() {
@Override
public void execute(BranchCommitRequest request, BranchCommitResponse response)
throws TransactionException {
//分支提交
doBranchCommit(request, response);
}
}, request, response);
return response;
}
@Override
public BranchRollbackResponse handle(BranchRollbackRequest request) {
//分支回滚响应体
BranchRollbackResponse response = new BranchRollbackResponse();
//模板方法执行正常/异常情况响应赋值
exceptionHandleTemplate(new AbstractCallback() {
@Override
public void execute(BranchRollbackRequest request, BranchRollbackResponse response)
throws TransactionException {
//分支回滚
doBranchRollback(request, response);
}
}, request, response);
return response;
}
protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response)
throws TransactionException {
String xid = request.getXid();
long branchId = request.getBranchId();
String resourceId = request.getResourceId();
//一阶段初始化好的上下文参数内容
String applicationData = request.getApplicationData();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData);
}
//委托给ResourceManager
BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId,
applicationData);
response.setXid(xid);
response.setBranchId(branchId);
response.setBranchStatus(status);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch commit result: " + status);
}
}
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();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch Rollbacking: " + xid + " " + branchId + " " + resourceId);
}
//委托给ResourceManager
BranchStatus status = getResourceManager().branchRollback(request.getBranchType(), xid, branchId, resourceId,
applicationData);
response.setXid(xid);
response.setBranchId(branchId);
response.setBranchStatus(status);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Branch Rollbacked result: " + status);
}
}
}
ResourceManager也是通过spi加载的,委托给DefaultResourceManager,最终定位到TCCResourceManager
public class TCCResourceManager extends AbstractResourceManager {
@Override
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
//通过resource名称获取到具体的TCCResource对象,
//在客户端GlobalTransactionScanner扫描的时候就将各分支方法的@TwoPhaseBusinessAction解析成TCCResource注册到map中
TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
if (tccResource == null) {
throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
}
//目标对象
Object targetTCCBean = tccResource.getTargetBean();
//指定的commit方法
Method commitMethod = tccResource.getCommitMethod();
if (targetTCCBean == null || commitMethod == null) {
throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
}
try {
//BusinessActionContext
//将TC server端返回的applicationData、xid等组装成BusinessActionContext对象,供2阶段commit方法使用
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
applicationData);
//支持在try方法上的@TwoPhaseBusinessAction中commitArgsClasses + commit方法参数注解@BusinessActionContextParameter 自定义2阶段commmit方法入参
//以@BusinessActionContextParameter的value属性作为key,从businessActionContext中获取到json string,再反序列为commitArgsClasses指定类型对象
Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext);
Object ret;
boolean result;
// add idempotent and anti hanging
// 幂等、空回滚、防悬挂处理
if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
try {
result = TCCFenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args);
} catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
throw e.getCause();
}
} else {
//直接执行commit方法
ret = commitMethod.invoke(targetTCCBean, args);
if (ret != null) {
if (ret instanceof TwoPhaseResult) {
result = ((TwoPhaseResult)ret).isSuccess();
} else {
result = (boolean)ret;
}
} else {
result = true;
}
}
LOGGER.info("TCC resource commit result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable;
} catch (Throwable t) {
String msg = String.format("commit TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
LOGGER.error(msg, t);
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
}
}
}
TCCFenceHandler.commitFence
public class TCCFenceHandler {
public static boolean commitFence(Method commitMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args) {
//开启事务保证tcc_fence_log的操作和业务事务绑定再一起
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
//commit操作前,先查询一下有没有该分支一阶段执行记录
TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
//没有记录,说明一阶段都未执行,不能执行commit,直接报错。报错后,等待重试
if (tccFenceDO == null) {
throw new TCCFenceException(String.format("TCC fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.RecordAlreadyExists);
}
//幂等控制:分支状态已提交,说明是重试执行,直接返回成功
if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
return true;
}
//理论上讲,不会存在这种情况。会重试
if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
}
return false;
}
//分支记录存在且还是初始化状态,正常执行commmit中业务逻辑,更新分支记录的状态为COMMITTED
return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
}
});
}
}
public class TCCResourceManager extends AbstractResourceManager {
@Override
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
//通过resource名称获取到具体的TCCResource对象,
//在客户端GlobalTransactionScanner扫描的时候就将各分支方法的@TwoPhaseBusinessAction解析成TCCResource注册到map中
TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
if (tccResource == null) {
throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
}
//目标对象
Object targetTCCBean = tccResource.getTargetBean();
//指定的rollback方法
Method rollbackMethod = tccResource.getRollbackMethod();
if (targetTCCBean == null || rollbackMethod == null) {
throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
}
try {
//BusinessActionContext
//将TC server端返回的applicationData、xid等组装成BusinessActionContext对象,供2阶段rollback方法使用
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
applicationData);
//支持在try方法上的@TwoPhaseBusinessAction中rollbackArgsClasses + rollback方法参数注解的@BusinessActionContextParameter 自定义2阶段rollback方法入参
//以@BusinessActionContextParameter的value属性作为key,从businessActionContext中获取到json string,再反序列为rollbackArgsClasses指定类型对象
Object[] args = this.getTwoPhaseRollbackArgs(tccResource, businessActionContext);
Object ret;
boolean result;
// add idempotent and anti hanging
// 幂等、空回滚、防悬挂处理
if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
try {
result = TCCFenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId,
args, tccResource.getActionName());
} catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
throw e.getCause();
}
} else {
//直接执行rollback方法
ret = rollbackMethod.invoke(targetTCCBean, args);
if (ret != null) {
if (ret instanceof TwoPhaseResult) {
result = ((TwoPhaseResult)ret).isSuccess();
} else {
result = (boolean)ret;
}
} else {
result = true;
}
}
LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable;
} catch (Throwable t) {
String msg = String.format("rollback TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
LOGGER.error(msg, t);
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
}
}
}
TCCFenceHandler.rollbackFence
public class TCCFenceHandler {
public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args, String actionName) {
//开启事务保证tcc_fence_log的操作和业务事务绑定再一起
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
//rollback操作前,先查询一下有没有该分支一阶段执行记录
TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
// non_rollback
// 空回滚&&防悬挂:没有记录,说明一阶段try未执行,或者本地事务回滚。先insert一条分支记录,确保后到的try方法不会再执行
// 如果insert成功,说明try还没有执行,空回滚成功
// 如果insert失败,说明try正在执行,抛出异常,等待TC重试即可
if (tccFenceDO == null) {
boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED);
LOGGER.info("Insert tcc fence record result: {}. xid: {}, branchId: {}", result, xid, branchId);
if (!result) {
throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.InsertRecordError);
}
return true;
} else {
// 幂等控制:如果已经rollback,直接返回成功
if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
return true;
}
// 理论上不会出现这种情况
if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
}
return false;
}
}
// 分支记录存在且还是初始化状态,正常执行rollback中业务逻辑,更新分支记录的状态为ROLLBACKED
return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
}
});
}
}