最近学习了分布式框架TCC,为了自己不忘记TCC的运行方式,特次记录下TCC架构源码解析。
TCC的概念跟系统架构不多描述,不懂的直接百度。
先简单的写下TCC架构的代码用例:方法A为主服务,是调用者。
@Compensable(confirmMethod = "confirmA",cancelMethod = "cancelA")
@Transactional
public void tryA(String args) throws Exception{
//执行try操作的代码 对数据进行加锁操作
。。。
//远程调用方法B
B(null,args);
// 远程调用方法C
C(null,args)
}
@Transactional
public void confirmA(String args){
//真正执行资源业务操作的代码
}
@Transactional
public void cancelA(String args){
//对加锁资源进行释放操作
}
远程调用方法B:
@Compensable(confirmMethod = "confirmB",cancelMethod = "cancelB")
@Transactional
public void tryB(TransactionContext transactionContext, String[] args ) {
//对业务资源进行加锁
}
@Transactional
public void confirmB(TransactionContext transactionContext, String[] args ) {
//进行业务执行
}
@Transactional
public void cancelB(TransactionContext transactionContext, String[] args ) {
//对加锁的业务资源进行解锁
}
远程调用方法C:
@Compensable(confirmMethod = "confirmC",cancelMethod = "cancelC")
@Transactional
public void tryC(TransactionContext transactionContext, String[] args ) {
//对业务资源进行加锁
}
@Transactional
public void confirmC(TransactionContext transactionContext, String[] args ) {
//进行业务执行
}
@Transactional
public void cancelC(TransactionContext transactionContext, String[] args ) {
//对加锁的业务资源进行解锁
}
作为参数的TransactionContext 是框架中自定义 事务上下文类,主要定义了事务id(全局事务id,分支事务id)跟事务状态字段,代码如下:
public class TransactionContext implements Serializable {
private static final long serialVersionUID = -8199390103169700387L;
//事务Id
private TransactionXid xid;
//事务状态
private int status;
/**
* 附加属性.
*/
private Map<String, String> attachments = new ConcurrentHashMap<String, String>();
/**
* 参与者列表.
*/
private List<Participant> participants = new ArrayList<Participant>();
public TransactionContext() {
}
/**
* 构建事务上下文对像.
* @param xid
* @param status
*/
public TransactionContext(TransactionXid xid, int status) {
this.xid = xid;
this.status = status;
}
...//get set方法
}
TransactionXid 类代码如下:
public class TransactionXid implements Xid, Serializable {
private static final long serialVersionUID = -6817267250789142043L;
/**
* XID 的格式标识符
*/
private int formatId = 1;
/**
* 全局事务ID.
*/
private byte[] globalTransactionId;
/**
* 分支限定符.
*/
private byte[] branchQualifier;
public TransactionXid() {
globalTransactionId = uuidToByteArray(UUID.randomUUID());
branchQualifier = uuidToByteArray(UUID.randomUUID());
}
public TransactionXid(byte[] globalTransactionId) {
this.globalTransactionId = globalTransactionId;
branchQualifier = uuidToByteArray(UUID.randomUUID());
}
public TransactionXid(byte[] globalTransactionId, byte[] branchQualifier) {
this.globalTransactionId = globalTransactionId;
this.branchQualifier = branchQualifier;
}
...//剩下的不重要
}
说完了应用代码实例 跟 TransactionContext 事务上下文类。现在开始进入主题!。
TCC架构最重要的两个拦截器
<!-- 可补偿事务拦截器 -->
<bean id="compensableTransactionInterceptor"
class="org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor">
<property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
</bean>
<!-- 资源协调拦截器 -->
<bean id="resourceCoordinatorInterceptor"
class="org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor">
<property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
</bean>
<!-- TCC补偿切面 --> <!--拦截带有@annotation(org.mengyun.tcctransaction.Compensable)的方法-->
<bean id="tccCompensableAspect" class="org.mengyun.tcctransaction.spring.TccCompensableAspect">
<property name="compensableTransactionInterceptor" ref="compensableTransactionInterceptor"/>
</bean>
<!-- TCC事务上下文切面 --><!--拦截execution(public**(org.mengyun.tcctransaction.api.TransactionContext,..))或者@annotation(org.mengyun.tcctransaction.Compensable)-->
<bean id="transactionContextAspect" class="org.mengyun.tcctransaction.spring.TccTransactionContextAspect">
<property name="resourceCoordinatorInterceptor" ref="resourceCoordinatorInterceptor"/>
</bean>
可补偿事务拦截器:org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor 用来保证事务的正确执行,。
资源协调拦截器:org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor 用来保证资源的正确调用。
在tryA被执行的时候,由于tryA方法带有@Compensable标签,所以先被TccCompensableAspect切面拦截
/**
* 定义环绕通知(在一个方法执行之前和执行之后运行,第一个参数必须是 ProceedingJoinPoint类型,pjp将包含切点拦截的方法的参数信息)
* @param pjp
* @throws Throwable
*/
@Around("compensableService()")
public Object interceptCompensableMethod(ProceedingJoinPoint pjp) throws Throwable {
LOG.debug("==>interceptCompensableMethod");
return compensableTransactionInterceptor.interceptCompensableMethod(pjp);
}
继续看,CompensableTransactionInterceptor 这个时候开始发挥作用。
/**
* 拦截补偿方法.
* @param pjp
* @throws Throwable
*/
public Object interceptCompensableMethod(ProceedingJoinPoint pjp) throws Throwable {
// 从拦截方法的参数中获取事务上下文 /这个时候还没有事务上下文 所以transactionContext = =null
TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
// 计算可补偿事务方法类型 这里根据是否有注解 跟事务上下文是否为空判断 该方法类型
MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, true);
logger.debug("==>interceptCompensableMethod methodType:" + methodType.toString());
switch (methodType) {
case ROOT:
return rootMethodProceed(pjp); // 主事务方法的处理(没有transactionContext参数)
case PROVIDER:
return providerMethodProceed(pjp, transactionContext); // 服务提供者事务方法处理
default:
return pjp.proceed(); // 其他的方法都是直接执行
}
}
由于transactionContext == null 而且 方法上标有注解 所以methodType == ROOT,代表tryA是根方法,也就是发起者所以查看rootMethodProceed(pjp);
/**
* 主事务方法的处理.
* @param pjp
* @throws Throwable
*/
private Object rootMethodProceed(ProceedingJoinPoint pjp) throws Throwable {
logger.debug("==>rootMethodProceed");
transactionConfigurator.getTransactionManager().begin(); // 事务开始(创建事务日志记录,并在当前线程缓存该事务日志记录)
//这一步创建一个status 为TRYING 的transactionType为ROOT事务上下文Transaction对象
//并将该Transaction保存到数据库 放置当前线程变量中
Object returnValue = null; // 返回值
try {
logger.debug("==>rootMethodProceed try begin");
returnValue = pjp.proceed(); // Try (开始执行被拦截的方法,或进入下一个拦截器处理逻辑)
logger.debug("==>rootMethodProceed try end");
} catch (OptimisticLockException e) {
logger.warn("==>compensable transaction trying exception.", e);
throw e; //do not rollback, waiting for recovery job
} catch (Throwable tryingException) {
logger.warn("compensable transaction trying failed.", tryingException);
transactionConfigurator.getTransactionManager().rollback();
throw tryingException;
}
logger.debug("===>rootMethodProceed begin commit()");
transactionConfigurator.getTransactionManager().commit(); // Try检验正常后提交(事务管理器在控制提交):Confirm
return returnValue;
}
注意 这个时候执行到 returnValue = pjp.proceed();之前;
当前TCC事务上下文为:
执行到 returnValue = pjp.proceed(),会被TccTransactionContextAspect拦截 ,执行以下方法。
/**
* 定义环绕通知(在一个方法执行之前和执行之后运行,第一个参数必须是 ProceedingJoinPoint类型,方法的调用者得到的返回值就是环绕通知返回的值)
* @param pjp
* @throws Throwable
*/
@Around("transactionContextCall()")
public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
return resourceCoordinatorInterceptor.interceptTransactionContextMethod(pjp);
}
然后看资源协调拦截器resourceCoordinatorInterceptor.interceptTransactionContextMethod(pjp)方法:
/**
* 拦截事务上下文方法.
* @param pjp
* @throws Throwable
*/
public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
// 获取当前事务
Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction();
// Trying(判断是否Try阶段的事务)
if (transaction != null && transaction.getStatus().equals(TransactionStatus.TRYING)) {
LOG.debug("==>TransactionStatus:" + transaction.getStatus().toString());
// 从参数获取事务上下文 从tryA参数中获取到的transactionContext 还是null
TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
// 获取事务补偿注解
Compensable compensable = getCompensable(pjp);
// 计算方法类型 methodType == root
MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, compensable != null ? true : false);
LOG.debug("==>methodType:" + methodType.toString());
switch (methodType) {
case ROOT:
generateAndEnlistRootParticipant(pjp); // 生成和登记根参与者
break;
case CONSUMER:
generateAndEnlistConsumerParticipant(pjp); // 生成并登记消费者的参与者
break;
case PROVIDER:
generateAndEnlistProviderParticipant(pjp); // 生成并登记服务提供者的参与者
break;
}
}
LOG.debug("==>pjp.proceed(pjp.getArgs())");
return pjp.proceed(pjp.getArgs()); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
}
查看 generateAndEnlistRootParticipant(pjp); 他是生成一个包含confirmA跟cancelA的Participant,并将其加入到transaction中,保存到A方法所指定的数据库。
/**
* 生成和登记根参与者.
* @param pjp
* @return
*/
private Participant generateAndEnlistRootParticipant(ProceedingJoinPoint pjp) {
LOG.debug("==>generateAndEnlistRootParticipant(ProceedingJoinPoint pjp)");
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Compensable compensable = getCompensable(pjp);
String confirmMethodName = compensable.confirmMethod(); // 确认方法
String cancelMethodName = compensable.cancelMethod(); // 取消方法
Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction(); // 获取当前事务
TransactionXid xid = new TransactionXid(transaction.getXid().getGlobalTransactionId()); // 使用全局事务ID和新的分支事务限定符号,生成新的事务Xid
LOG.debug("==>TransactionXid:" + TransactionXid.byteArrayToUUID(xid.getGlobalTransactionId()).toString()
+ "|" + TransactionXid.byteArrayToUUID(xid.getBranchQualifier()).toString());
// 获取到目标类(最好做成独立的类)
Class targetClass = ReflectionUtils.getDeclaringType(pjp.getTarget().getClass(), method.getName(), method.getParameterTypes());
// 构建确认方法的提交上下文(相同的参数)
InvocationContext confirmInvocation = new InvocationContext(targetClass,
confirmMethodName,
method.getParameterTypes(), pjp.getArgs());
// 构建取消方法的提交上下文(相同的参数)
InvocationContext cancelInvocation = new InvocationContext(targetClass,
cancelMethodName,
method.getParameterTypes(), pjp.getArgs());
// 构建参与者对像
Participant participant =
new Participant(
xid,
new Terminator(confirmInvocation, cancelInvocation));
transaction.enlistParticipant(participant); // 加入参与者
TransactionRepository transactionRepository = transactionConfigurator.getTransactionRepository();
transactionRepository.update(transaction); // 更新事务信息(加入了事务参与者,包含了触发confirm或cancel方法的参数信息)
return participant;
}
此时事务信息上下文:
继续往下执行pjp.proceed(pjp.getArgs());
这时候执行到tryB方法,方法B参数中含有Transaction类,所以被TccCompensableAspect切面拦截,执行
/**
* 拦截事务上下文方法.
* @param pjp
* @throws Throwable
*/
public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
// 获取当前事务
Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction();
// Trying(判断是否Try阶段的事务)
if (transaction != null && transaction.getStatus().equals(TransactionStatus.TRYING)) {
LOG.debug("==>TransactionStatus:" + transaction.getStatus().toString());
// 从参数获取事务上下文 从tryA参数中获取到的transactionContext 还是null
TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
// 获取事务补偿注解
Compensable compensable = getCompensable(pjp);
// 计算方法类型
MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, compensable != null ? true : false);
LOG.debug("==>methodType:" + methodType.toString());
switch (methodType) {
case ROOT:
generateAndEnlistRootParticipant(pjp); // 生成和登记根参与者
break;
case CONSUMER:
generateAndEnlistConsumerParticipant(pjp); // 生成并登记消费者的参与者
break;
case PROVIDER:
generateAndEnlistProviderParticipant(pjp); // 生成并登记服务提供者的参与者
break;
}
}
LOG.debug("==>pjp.proceed(pjp.getArgs())");
return pjp.proceed(pjp.getArgs()); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
}
这个时候methodType为CONSUMER,执行generateAndEnlistConsumerParticipant(pjp),生成提供服务者的参与者,此时事务上下文:
开始远程执行tryB方法,远程方法tryB被执行的时候,由于有注释和transaction参数,所以一样会被两个切面拦截,由于过程一样,第一个切面拦截时,其methodType 为PROVIDER,执行providerMethodProceed(pjp, transactionContext);方法
private Object providerMethodProceed(ProceedingJoinPoint pjp, TransactionContext transactionContext) throws Throwable {
logger.debug("==>providerMethodProceed transactionStatus:" + TransactionStatus.valueOf(transactionContext.getStatus()).toString());
switch (TransactionStatus.valueOf(transactionContext.getStatus())) {
case TRYING:
logger.debug("==>providerMethodProceed try begin");
// 基于全局事务ID扩展创建新的分支事务,并存于当前线程的事务局部变量中.
transactionConfigurator.getTransactionManager().propagationNewBegin(transactionContext);
logger.debug("==>providerMethodProceed try end");
return pjp.proceed(); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
case CONFIRMING:
try {
logger.debug("==>providerMethodProceed confirm begin");
// 找出存在的事务并处理.
transactionConfigurator.getTransactionManager().propagationExistBegin(transactionContext);
transactionConfigurator.getTransactionManager().commit(); // 提交
logger.debug("==>providerMethodProceed confirm end");
} catch (NoExistedTransactionException excepton) {
//the transaction has been commit,ignore it.
}
break;
case CANCELLING:
try {
logger.debug("==>providerMethodProceed cancel begin");
transactionConfigurator.getTransactionManager().propagationExistBegin(transactionContext);
transactionConfigurator.getTransactionManager().rollback(); // 回滚
logger.debug("==>providerMethodProceed cancel end");
} catch (NoExistedTransactionException exception) {
//the transaction has been rollback,ignore it.
}
break;
}
Method method = ((MethodSignature) (pjp.getSignature())).getMethod();
return ReflectionUtils.getNullValue(method.getReturnType());
}
此时事务上下文的status为TRYING,远程方法B这边跟局传过来的transactionContext中的全局事务Id开启一个新的事务。
继续被TccTransactionContextAspect切面拦截,生成生成并登记服务提供者的参与者,此时事务上下文:
然后远程服务tryB被真正执行完,回到tryA方法,开始执行tryC方法,同样的经过切面拦截,事务上下文变为:
事务上下文构建完之后,这个时候 A方法中的切面拦截方法还没有结束,才刚刚执行完pjp.proceed();方法
然后开始往下执行,transactionConfigurator.getTransactionManager().commit();
/**
* 提交.
*/
public void commit() {
LOG.debug("==>TransactionManager commit()");
Transaction transaction = getCurrentTransaction();
transaction.changeStatus(TransactionStatus.CONFIRMING);
LOG.debug("==>update transaction status to CONFIRMING");
transactionConfigurator.getTransactionRepository().update(transaction);
try {
LOG.info("==>transaction begin commit()");
transaction.commit();
transactionConfigurator.getTransactionRepository().delete(transaction);
} catch (Throwable commitException) {
LOG.error("compensable transaction confirm failed.", commitException);
throw new ConfirmingException(commitException);
}
}
执行 transaction.commit();操作
/**
* 事务提交(包含此事务的所有参与者的逐个提交,在TransactionManager中被调用).
*/
public void commit() {
LOG.debug("==>Transaction.commit()");
for (Participant participant : participants) {
participant.commit();
}
}
participant.commit();方法就是执行各个participant的confirm方法,所以这个时候事务上下文中所有participant的confirm方法会被执行:
各个的confirm操作执行完之后,业务代码基本都执行完了,最后执行 transactionConfigurator.getTransactionRepository().delete(transaction);
将数据库中保存的transaction上下文删除。
如果在执行tryA,tryB,tryC方法的时候出现异常:
这里有一个事务回滚的操作,即事务上下文中的各个participant调用cancel方法,来保分布式证事务的一致性。
下一篇记录TCC事务如果confirmA,confirmB执行了,但是confirmC失败之后的补偿操作。