版本:1.2.0
TCC是一种资源服务化的两阶段提交协议,用户需要实现Try、Confirm/Cancel接口。通过一阶段的Try操作决定执行Confirm(确认)或者Cancel(取消)操作。
Seata TCC三种接口去对资源进行管理
- prepare资源预留
- commit资源提交
- rollback 资源回滚
然后将分支事务纳入到全局事务中,对全局事务管理。
使用
通过与Spring的结合使用了解Seata TCC模式的执行流程。
- 定义接口,通过实现接口去进行资源的预留提交和回滚操作,每个分支事务都是RM
public interface TccActionOne {
@TwoPhaseBusinessAction(name="TccActionOne",commitMethod = "commit",rollbackMethod = "rollback")
public boolean prepare(BusinessActionContext actionContext,int num);
public boolean commit(BusinessActionContext actionContext);
public boolean rollback(BusinessActionContext actionContext);
}
每个预留资源接口添加@TwoPhaseBusinessAction(name="TccActionOne",commitMethod = "commit",rollbackMethod = "rollback")
注解去定义各接口操作
- 调用接口,添加
@GlobalTransactional
将分支事务纳入全局事务
@GlobalTransactional
public String doTransactionCommit(){
//第一个TCC 事务参与者
boolean result = tccActionOne.prepare(null, 1);
if(!result){
throw new RuntimeException("TccActionOne failed.");
}
//第二个TCC 事务参与者
result = tccActionTwo.prepare(null, "two");
if(!result){
throw new RuntimeException("TccActionTwo failed.");
}
return RootContext.getXID();
}
- 注入
GlobalTransactionScanner
,初始化TMClinet和RmClient及其他模式配置的初始化
@Bean
public GlobalTransactionScanner globalTransactionScanner(){
return new GlobalTransactionScanner("ijcf_tx_group");
}
初始化
通过GlobalTransactionScanner
进行初始化
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
implements InitializingBean, ApplicationContextAware,
DisposableBean
- 实现
InitializingBean
的afterPropertiesSet
方法,会在类实例化时执行,主要是用于初始化TM\RM客户端并向Server注册
public void afterPropertiesSet() {
initClient();
}
private void initClient() {
if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
}
//init TM
TMClient.init(applicationId, txServiceGroup);
//init RM
RMClient.init(applicationId, txServiceGroup);
//注册钩子
registerSpringShutdownHook();
}
- 实现
ApplicationContextAware
的setApplicationContext
方法获得spring容器 - 实现
DisposableBean
的destroy
方法,在容器关闭时进行操作 - 继承
AbstractAutoProxyCreator
类重写wrapIfNecessary
进行切面代理。
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//是否禁用了全局事务
if (disableGlobalTransaction) {
return bean;
}
try {
synchronized (PROXYED_SET) {
//检查当前beanName是否已经处理过 如果处理过本次就不处理
if (PROXYED_SET.contains(beanName)) {
return bean;
}
interceptor = null;
//check TCC proxy
//校验是否为TCC分支事务类
//会在这里进行资源的注册
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
//TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
//创建拦截器
interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
} else {
Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
//判断类方法中是否添加@GlobalTransactional 或@GlobalLock注解,判断当前是否全局事务类
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
if (interceptor == null) {
//创建全局事务拦截器
interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationFactory.getInstance().addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener) interceptor);
}
}
LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
if (!AopUtils.isAopProxy(bean)) {
//执行父类方法 添加 拦截器
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {
AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
for (Advisor avr : advisor) {
advised.addAdvisor(0, avr);
}
}
PROXYED_SET.add(beanName);
return bean;
}
} catch (Exception exx) {
throw new RuntimeException(exx);
}
}
通过上面的源码可以看出有两个核心拦截器,TccActionInterceptor
TCC模式拦截器和GlobalTransactionalInterceptor
全局事务拦截器,这两个拦截器为方法拦截器,会在类调用方法时执行其invoke
方法。
TCC流程
调用doTransactionCommit
方法开始分布式事务执行。通过上面分析会执行GlobalTransactionalInterceptor
全局事务拦截器的invoke
方法
GlobalTransactionalInterceptor.invoke
@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);
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, GlobalTransactional.class);
final GlobalLock globalLockAnnotation = getAnnotation(method, GlobalLock.class);
//GlobalTransactional全局事务注解切面逻辑
if (!disable && globalTransactionalAnnotation != null) {
return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
} else if (!disable && globalLockAnnotation != null) {
//GlobalLock 注解切面逻辑
return handleGlobalLock(methodInvocation);
} else {
return methodInvocation.proceed();
}
}
注:GlobalLock
注解是为了保证事务隔离,并不会加入到全局事务
GlobalTransactionalInterceptor.handleGlobalTransaction
private Object handleGlobalTransaction(final MethodInvocation methodInvocation,
final GlobalTransactional globalTrxAnno) throws Throwable {
try {
return transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
...
});
} catch (TransactionalExecutor.ExecutionException e) {
...
}
}
通过TransactionalExecutor
执行方法,而TransactionalTemplate.execute
才是开始事务的关键。
transactionalTemplate.execute
public Object execute(TransactionalExecutor business) throws Throwable {
...
// 1.1 获取全局事务如果不存在则创建
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
...
try {
...
try {
// 2. 开启事务
beginTransaction(txInfo, tx);
Object rs = null;
try {
// 2.1执行业务代码
rs = business.execute();
} catch (Throwable ex) {
// 3.异常回滚
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 4. 提交
commitTransaction(tx);
return rs;
} finally {
//5. 清理执行结束钩子
triggerAfterCompletion();
cleanUp();
}
}
主要流程:
- 1.创建全局事务
- 2.TM向TC发送请求开启全局事务
- 3.执行业务代码
- 4.根据执行结果回滚或提交
开启事务
TransactionalTemplate.beginTransaction
private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
...
//执行前钩子
triggerBeforeBegin();
//开启事务
tx.begin(txInfo.getTimeOut(), txInfo.getName());
//执行后钩子
triggerAfterBegin();
...
}
GlobalTransaction.begin
public void begin(int timeout, String name) throws TransactionException {
...
// 通过TransactionManager开启事务
xid = transactionManager.begin(null, null, name, timeout);
status = GlobalStatus.Begin;
//绑定到RootContext
RootContext.bind(xid);
...
}
开启全局事务
TransactionManager.begin
:TransactionManager,TM 用于开启和管理全局事务,默认实现DefaultTransactionManager
@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
//创建一个全局事务开启请求
GlobalBeginRequest request = new GlobalBeginRequest();
request.setTransactionName(name);
request.setTimeout(timeout);
//向服务端发送全局事务开启请求,服务端会返回全局事务ID XID
GlobalBeginResponse response = (GlobalBeginResponse)syncCall(request);
if (response.getResultCode() == ResultCode.Failed) {
throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
}
return response.getXid();
}
上篇文章介绍了服务端DefaultCore
是核心实现,驱动全局事务的开启提交回滚操作。服务端接收请求响应
DefaultCore.begin
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
//创建一个全局事务上下文
GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
timeout);
session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
//开启事务
session.begin();
eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC,
session.getTransactionName(), session.getBeginTime(), null, session.getStatus()));
//返回全局事务ID XID
return session.getXid();
}
执行业务代码
**
上文介绍在TCC分支会创建拦截器TccActionInterceptor
,在执行分支的业务代码时会调用其invoke
方法。
public Object invoke(final MethodInvocation invocation) throws Throwable {
if (!RootContext.inGlobalTransaction()) {
//不在事务中
return invocation.proceed();
}
Method method = getActionInterfaceMethod(invocation);
//获取TwoPhaseBusinessAction注解信息
TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
//执行try操作
if (businessAction != null) {
String xid = RootContext.getXID();
//清理核心上文
RootContext.unbind();
RootContext.bindInterceptorType(xid, BranchType.TCC);
try {
Object[] methodArgs = invocation.getArguments();
//拦截器Handler 进行分支注册及程序执行
Map<String, Object> ret = actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction,
invocation::proceed);
//返回结果
return ret.get(Constants.TCC_METHOD_RESULT);
} finally {
//恢复核心上下文
RootContext.unbindInterceptorType();
RootContext.bind(xid);
}
}
return invocation.proceed();
}
ActionInterceptorHandler.proceed
public Map<String, Object> proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
Callback<Object> targetCallback) throws Throwable {
Map<String, Object> ret = new HashMap<>(4);
//TCC name
String actionName = businessAction.name();
//构造业务上下文
BusinessActionContext actionContext = new BusinessActionContext();
actionContext.setXid(xid);
//设置name
actionContext.setActionName(actionName);
//RM分支注册,服务端响应BranchId
String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
actionContext.setBranchId(branchId);
//业务上下文BusinessActionContext 添加到方法参数中
//上文使用中--tccActionOne.prepare(null, 1);参数需要设置为nul
Class<?>[] types = method.getParameterTypes();
int argIndex = 0;
for (Class<?> cls : types) {
if (cls.getName().equals(BusinessActionContext.class.getName())) {
arguments[argIndex] = actionContext;
break;
}
argIndex++;
}
//返回最终参数
ret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);
//执行业务方法
ret.put(Constants.TCC_METHOD_RESULT, targetCallback.execute());
return ret;
}
protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction,
BusinessActionContext actionContext) {
String actionName = actionContext.getActionName();
String xid = actionContext.getXid();
//获取扩展参数 @BusinessActionContextParameter注解参数可以在Context中获取
Map<String, Object> context = fetchActionRequestContext(method, arguments);
context.put(Constants.ACTION_START_TIME, System.currentTimeMillis());
//初始化业务上下文
initBusinessContext(context, method, businessAction);
//Init running environment context
initFrameworkContext(context);
actionContext.setActionContext(context);
//应用数据
Map<String, Object> applicationContext = new HashMap<>(4);
applicationContext.put(Constants.TCC_ACTION_CONTEXT, context);
String applicationContextStr = JSON.toJSONString(applicationContext);
try {
//RM向TC注册分支,TC返回分支Id
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid,
applicationContextStr, null);
return String.valueOf(branchId);
} catch (Throwable t) {
String msg = String.format("TCC branch Register error, xid: %s", xid);
LOGGER.error(msg, t);
throw new FrameworkException(t, msg);
}
}
TCC分支事务执行会先执行切面拦截器invoke方法
,其实际过程是通过切面处理器ActionInterceptorHandler.proceed
方法去完成分支注册方法执行。而分支注册是默认RM实现DefaultResourceManager.branchRegister
完成。DefaultResourceManager
主要管理各类RM,而分支注册是根据不同模式下的Rm去完成分支注册。TCC模式下通过TCCResourceManager
完成分支注册,其内部调用父类AbstractResourceManager.branchRegister
最终完成分支注册。
DefaultResourceManager.branchRegister
public Long branchRegister(BranchType branchType, String resourceId,
String clientId, String xid, String applicationData, String lockKeys)
throws TransactionException {
//判断分支类型 TCC / SAGA 去完成注册
return getResourceManager(branchType).branchRegister(branchType, resourceId, clientId, xid, applicationData,
lockKeys);
}
AbstractResourceManager.branchRegister
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException {
...
BranchRegisterRequest request = new BranchRegisterRequest();
request.setXid(xid);
request.setLockKey(lockKeys);
request.setResourceId(resourceId);
request.setBranchType(branchType);
request.setApplicationData(applicationData);
//向服务端发起分支事务注册 ,服务端响应
BranchRegisterResponse response = (BranchRegisterResponse) RmRpcClient.getInstance().sendMsgWithResponse(request);
if (response.getResultCode() == ResultCode.Failed) {
throw new RmTransactionException(response.getTransactionExceptionCode(), String.format("Response[ %s ]", response.getMsg()));
}
return response.getBranchId();
...
}
服务端
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
//校验全局Session
GlobalSession globalSession = assertGlobalSessionNotNull(xid, false);
return SessionHolder.lockAndExecute(globalSession, () -> {
globalSessionStatusCheck(globalSession);
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
//创建分支session
BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, branchType, resourceId,
applicationData, lockKeys, clientId);
//锁定当前session
branchSessionLock(globalSession, branchSession);
try {
//把当前分支事务添加到全局事务中
globalSession.addBranch(branchSession);
} catch (RuntimeException ex) {
//解除锁定
branchSessionUnlock(branchSession);
...
}
//返回分支ID
return branchSession.getBranchId();
});
}
这里就完成了方法的执行
事务回滚
方法执行出现异常,会向TC服务端发起全局回滚请求,TC 在收到回滚请求后通知各事务分支进行分支回滚并返回TC分支回滚状态,事务回滚的方法与事务提交的逻辑一致
TransactionalTemplate.rollbackTransaction
->GlobalTransaction.rollback
->DefaultGlobalTrans.rollback
->DefaultTransactionManager.rollback
向服务器发送全局回滚请求
public GlobalStatus rollback(String xid) throws TransactionException {
//构造全局回滚请求
GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
globalRollback.setXid(xid);
//发送请求 TC响应结果
GlobalRollbackResponse response = (GlobalRollbackResponse)syncCall(globalRollback);
return response.getGlobalStatus();
}
服务器DefaultCore.rollback
public GlobalStatus rollback(String xid) throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
if (globalSession == null) {
return GlobalStatus.Finished;
}
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
// 锁定当前状态
boolean shouldRollBack = SessionHolder.lockAndExecute(globalSession, () -> {
globalSession.close(); // 关闭全局session注册
if (globalSession.getStatus() == GlobalStatus.Begin) {
globalSession.changeStatus(GlobalStatus.Rollbacking);
return true;
}
return false;
});
if (!shouldRollBack) {
return globalSession.getStatus();
}
doGlobalRollback(globalSession, false);
return globalSession.getStatus();
}
public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
boolean success = true;
// 开启回滚事件
eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC,
globalSession.getTransactionName(), globalSession.getBeginTime(), null, globalSession.getStatus()));
//是否为SAGA 使用SAGA模式回滚
if (globalSession.isSaga()) {
success = getCore(BranchType.SAGA).doGlobalRollback(globalSession, retrying);
} else {
//回滚各分支事务
for (BranchSession branchSession : globalSession.getReverseSortedBranches()) {
BranchStatus currentBranchStatus = branchSession.getStatus();
//一阶段失败不需要回滚,从全局事务中移除
if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
globalSession.removeBranch(branchSession);
continue;
}
try {
//分支回滚
BranchStatus branchStatus = branchRollback(globalSession, branchSession);
...
} catch (Exception ex) {
...
}
}
...
}
...
return success;
}
public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
try {
//构造分支回滚请求
BranchRollbackRequest request = new BranchRollbackRequest();
request.setXid(branchSession.getXid());
request.setBranchId(branchSession.getBranchId());
request.setResourceId(branchSession.getResourceId());
request.setApplicationData(branchSession.getApplicationData());
request.setBranchType(branchSession.getBranchType());
//向客户端TM发送请求回滚分支
return branchRollbackSend(request, globalSession, branchSession);
} catch (IOException | TimeoutException e) {
...
}
}
TCC RM执行分支回滚TCCResourceManager.branchRollback
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
...
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
applicationData);
//通过BusinessActionContext注解配置的rollback方法进行分支回滚
Object ret = rollbackMethod.invoke(targetTCCBean, businessActionContext);
LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", ret, xid, branchId, resourceId);
...
}
到这里也就完成了对全局事务与分支事务的回滚操作,事务提交操作与之一致,在分支提交时会调用预留的接口,从而完成分布式事务操作。