二TCC-transaction分布式事务关键组件--1Participant参与者、TransactionManager事务管理器
二 TCC-transaction分布式事务关键组件--1Participant参与者、TransactionManager事务管理器
二 TCC-transaction关键组件类及原理
2.1 框架原理
2.1.1 基本使用
在一个模块配置Tcc-transaction并使用,需要执行如下操作:
1.1 生效tcc-transaction
在spring项目中可以使用xml配置或者注解来生效tcc-transaction。
a. XML配置
<tcc:annotation-driven transaction-repository="transactionRepository"/>
b. 注解配置
@EnableTccTransaction
1.2 配置transaction repository
transaction repository用来保存事务日志,框架提供多种Transaction Repository:
在tcc-transaction-dubbo-sample中使用MemoryStoreTransactionRepository(没有持久化能力,仅可用于测试): <bean id="transactionRepository" class="org.mengyun.tcctransaction.repository.MemoryStoreTransactionRepository"> <property name="domain" value="TCC:DUBBO:CAPITAL:"/> </bean>
1.3 声明tcc接口
在接口方法上加上@EnableTcc(1.7.x新增注解,1.6.x是@Compensable)将提供的接口标记为tcc接口:
public interface CapitalTradeOrderService { @EnableTcc public String record(CapitalTradeOrderDto tradeOrderDto); }
1.4 配置TCC事务的Try、Confirm、Cancel方法:
在tcc接口实现上方法上添加@Compensable注解,设置confirmMethod和cancelMethod方法,分别为tcc的confirm和cancel方法。
@Override @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class) @Transactional public String record(CapitalTradeOrderDto tradeOrderDto) { ... }
在 TCC 里,一个业务活动可以有多个事务,每个业务操作归属于不同的事务,即一个事务可以包含多个业务操作。TCC-Transaction 将每个业务操作抽象成事务参与者,每个事务可以包含多个参与者。
参与者需要声明 try / confirm / cancel 三个类型的方法,和 TCC 的操作一一对应。在程序里,通过 @Compensable 注解标记在 try 方法上,并填写对应的 confirm / cancel 方法,示例代码如下:
// try @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class) public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {} // confirm public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {} // cancel public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}
try阶段是由业务方发起调用,而confirm和cancel由事务管理器自动调用。在这里,事务管理器就是CompensableTransactionInterceptor(通过组合的方式,里面含有的TransactionManager才是真正的事务管理器,TransactionManager的方法会被调用),confirm和cancel方法由它自动调用。
主要通过spring-aop拦截器CompensableTransactionInterceptor、ResourceCoordinatorInterceptor
CompensableTransactionInterceptor : 用于tcc事务的流程执行begin(try)、commit(confirm)、rollback(cancel)
ResourceCoordinatorInterceptor : 用于记录tcc事务的Participant(参与方)
2.1.2 框架基本流程
2.2 核心组件类
针对TCC-Transaction框架中涉及的关键组件,按照如图所示的模块,首先对各个关键组件进行分析:
2.2.1 事务参与者Participant与事务Transaction
关系如图:

TCC将事务和事务参与者,抽象为两个类Transaction和Participant。
一个transaction可以有多个participant参与者 Transaction---{participant1;participant2;participant3......} Transaction{ List<Participant> participants //事务对象中,用list保存当前事务的参与者 commit()/rollback() ;//事务提供的方法,通过for循环,遍历list中每个participant.commit/rollback方法实现方法提交、回滚 } Participant{ transactionXid ;//当前事务的全局id confirm/cancelInvocationContext;//confirm或者cancel方法调用上下文(包含调用的所有信息) Terminator ;// commit/rollback通过Terminator对象,完成方法的调用 TransactionContextEditorClass;//transactionContext事务上下文的编辑类,可以通过该类修改和获取事务上下文 //事务的提交与回滚,通过Terminator调用 commit/rollback{ Terminator.invoke(new TransactionContext,new Invocation,transactionContextEditorClass); } }
1 Transaction事务
关注核心的属性和方法
//事务对象需要持久存储,所以需要其实现序列化Serializable public class Transaction implements Serializable { private List<Participant> participants = new ArrayList<>(); //事务的参与者participants列表 private Map<String, Object> attachments = new ConcurrentHashMap<>(); //附加属性 private Date createTime = new Date(); private Xid xid; //事务编号,唯一地标识一个事务,UUID算法生成 private Xid rootXid; private String rootDomain; private TransactionType transactionType; //事务类型(包含ROOT根事务、BRANCH分支事务),表示当前事务时发起者还是参与者 private TransactionStatus status;//事务状态(包含trying、confirming、cancelling),表示当前事务在tcc执行的阶段 private Date lastUpdateTime = new Date(); private volatile int retriedCount = 0;//重试次数 private long version = 0L; //版本号,用于乐观锁更新事务 public Transaction() { } //该构造函数,创建trying阶段的分支branch事务 public Transaction(TransactionContext transactionContext) { this.xid = transactionContext.getXid(); this.rootXid = transactionContext.getRootXid(); this.rootDomain = transactionContext.getRootDomain(); this.status = TransactionStatus.TRYING; this.transactionType = TransactionType.BRANCH; } //该构造函数,创建trying阶段的root事务 public Transaction(Object uniqueIdentity, String rootDomain) { this.xid = TransactionXid.withUniqueIdentity(uniqueIdentity); this.status = TransactionStatus.TRYING; this.transactionType = TransactionType.ROOT; this.rootXid = xid; this.rootDomain = rootDomain; } //commit使用fori循环,调用每个参与者participant.commit public void commit() { for (Participant participant : participants) { if (!participant.getStatus().equals(ParticipantStatus.CONFIRM_SUCCESS)) { participant.commit(); //commit提交成功后,修改参与者的状态 participant.setStatus(ParticipantStatus.CONFIRM_SUCCESS); } } } public void rollback() { for (Participant participant : participants) { if (!participant.getStatus().equals(ParticipantStatus.CANCEL_SUCCESS)) { participant.rollback(); participant.setStatus(ParticipantStatus.CANCEL_SUCCESS); } } } //添加事务参与者 public void enlistParticipant(Participant participant) { participants.add(participant); }
(1)TransactionXid
其中,TransactionXid使用uuid算法生成全局唯一的事务id:
public static TransactionXid withUniqueIdentity(Object uniqueIdentity) { int formatId = Xid.AUTO; String xid = null; if (uniqueIdentity == null) { xid = FactoryBuilder.factoryOf(UUIDGenerator.class).getInstance().generate(); } else { xid = uniqueIdentity.toString(); formatId = Xid.CUSTOMIZED; } return new TransactionXid(formatId, xid); }
(2)TransactionStatus
public enum TransactionStatus { TRYING(1), CONFIRMING(2), CANCELLING(3), TRY_SUCCESS(11), TRY_FAILED(12);
(3)TransactionType
public enum TransactionType { ROOT(1), BRANCH(2);
2 Participant事务参与者
public class Participant implements Serializable { Class<? extends TransactionContextEditor> transactionContextEditorClass; //事务上下文编辑器类 private Xid rootXid; private String rootDomain; private Xid xid; //事务id private InvocationContext invocationContext; private ParticipantStatus status = ParticipantStatus.TRYING; //参与者状态 public Participant() { } public void rollback() { //利用执行器调用对应的rollback方法 Terminator.invoke(new TransactionContext(rootDomain, rootXid, xid, TransactionStatus.CANCELLING, status), new Invocation(invocationContext.getCancelMethodName(), invocationContext), transactionContextEditorClass); } public void commit() { Terminator.invoke(new TransactionContext(rootDomain, rootXid, xid, TransactionStatus.CONFIRMING, status), new Invocation(invocationContext.getConfirmMethodName(), invocationContext), transactionContextEditorClass); }
participant的xid事务id,通过该id关联上其所属的事务。同一个事务下的事务id相同,通过同id执行tcc的参与者的操作。
(1)ParticipantStatus
参与者的状态,标识当前事务参与者,在tcc执行过程中,所处于TCC阶段的状态
public enum ParticipantStatus { TRYING(1), CONFIRMING(2), CANCELLING(3), TRY_SUCCESS(11), TRY_FAILED(12), CONFIRM_SUCCESS(21), CANCEL_SUCCESS(31); private int id;
(2) TransactionContext事务上下文
public class TransactionContext implements Serializable { private static final long serialVersionUID = -8199390103169700387L; private Xid xid; private Xid rootXid; private String rootDomain; private TransactionStatus status = TransactionStatus.TRYING; private ParticipantStatus participantStatus = ParticipantStatus.TRYING; private Map<String, String> attachments = new ConcurrentHashMap<>();
(3) InvocationContext调用上下文
调用上下文,包含了当前调用的所有信息,包括目标类、参数类型、confirm和cancel方法名称
public class InvocationContext implements Serializable { private static final long serialVersionUID = -7969140711432461165L; private Class targetClass; private String confirmMethodName; private String cancelMethodName; private Class[] parameterTypes; private Object[] args;
(4)Invocation
调用,包括调用的方法名和InvocationContext
public class Invocation { private String methodName; private InvocationContext invocationContext; public Invocation(String methodName, InvocationContext invocationContext) { this.methodName = methodName; this.invocationContext = invocationContext; }
(5)Terminator执行器
public final class Terminator { public static Object invoke(TransactionContext transactionContext, Invocation invocation, Class<? extends TransactionContextEditor> transactionContextEditorClass) { //当invocation中方法名不为空,即有实际的调用方法时 if (StringUtils.isNotEmpty(invocation.getMethodName())) { //参与者对象类 Object target = FactoryBuilder.factoryOf(invocation.getTargetClass()).getInstance(); Method method = null; try { //参与者需要执行的方法(try/confirm/cancel) method = target.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); } catch (NoSuchMethodException e) { throw new SystemException(e); } //设置事务上下文transactionContext到transactionContextEditorClass上 FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance().set(transactionContext, target, method, invocation.getArgs()); try { //反射调用 return method.invoke(target, invocation.getArgs()); } catch (IllegalAccessException | InvocationTargetException e) { throw new SystemException(e); } finally { FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance().clear(transactionContext, target, method, invocation.getArgs()); } } return null; }
(6)TransactionContextEditor事务上下文编辑器
在TCC2.0版本以后,只有这个默认实现类,用threadLocal保存事务上下文对象。
public class ThreadLocalTransactionContextEditor implements TransactionContextEditor { @Override public TransactionContext get(Object target, Method method, Object[] args) { return TransactionContextHolder.getCurrentTransactionContext(); } @Override public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) { TransactionContextHolder.setCurrentTransactionContext(transactionContext); } public void clear(TransactionContext transactionContext, Object target, Method method, Object[] args) { TransactionContextHolder.clear(); } }
public class TransactionContextHolder { //创建当前线程变量ThreadLocal,保存事务上下文 private static ThreadLocal<TransactionContext> transactionContextThreadLocal = new ThreadLocal<>(); private TransactionContextHolder() { } public static TransactionContext getCurrentTransactionContext() { return transactionContextThreadLocal.get(); } public static void setCurrentTransactionContext(TransactionContext transactionContext) { transactionContextThreadLocal.set(transactionContext); } public static void clear() { transactionContextThreadLocal.remove(); } }
可知,TransactionContextEditor是将事务上下文保存在本地线程对象ThreadLocal中。
2.2.2 事务管理器TransactionManager
事务管理器类,主要提供事务的begin、commit、rollback和获取事务、participant的新增管理
public class TransactionManager { private static final ThreadLocal<Deque<Transaction>> CURRENT = new ThreadLocal<>(); private int threadPoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1; private int threadQueueSize = 1024; //TCC提供的异步执行Terminator线程池 private ExecutorService asyncTerminatorExecutorService = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(threadQueueSize), new ThreadPoolExecutor.AbortPolicy()); private ExecutorService asyncSaveExecutorService = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(threadQueueSize * 2), new ThreadPoolExecutor.CallerRunsPolicy()); private TransactionRepository transactionRepository; //事务的开启、commit、rollback public Transaction begin(Object uniqueIdentify) { public void commit(boolean asyncCommit) { public void rollback(boolean asyncRollback) { // public Transaction propagationNewBegin(TransactionContext transactionContext) public Transaction propagationExistBegin(TransactionContext transactionContext) //添加事务参与者Participant public void enlistParticipant(Participant participant) //注册事务Transaction private void registerTransaction(Transaction transaction)
然后,对于事务管理器提供的核心方法,进行分析:
(a)TransactionManager.begin开启事务
发起根事务TransactionType.ROOT---此时创建事务类型为ROOT的根事务,并存储、注册。
public Transaction begin(Object uniqueIdentify) { //TAG1 创建根事务 Transaction transaction = new Transaction(uniqueIdentify, this.transactionRepository.getDomain()); //为了性能调优,在创建transaction阶段,不着急持久化存储 if (transaction.getXid().getFormatId() == Xid.CUSTOMIZED) { //TAG2 对于自定义的xid,确保在TCC阶段之前只创建一次事务(所以对于自定义的xid,需要在tcc阶段开始前,先持久化存储) transactionRepository.create(transaction); } //TAG3 注册事务transaction registerTransaction(transaction); return transaction; }
TAG1:这里创建事务Transaction,使用如下构造函数:
public Transaction(Object uniqueIdentity, String rootDomain) { this.xid = TransactionXid.withUniqueIdentity(uniqueIdentity); this.status = TransactionStatus.TRYING; this.transactionType = TransactionType.ROOT; this.rootXid = xid; this.rootDomain = rootDomain; }
Transaction有两个构造函数,当前构造函数只被begin()调用,只创建TransactionType.ROOT根事务、且 TransactionStatus.TRYING创建的是TCC阶段的try阶段。
TAG2:transactionRepository.create(transaction) 存储事务。详细见后续
TAG3 注册事务transaction
(b)TransactionManager.registerTransaction注册事务
//事务管理器的本地线程变量,其内存储事务Transaction的队列 private static final ThreadLocal<Deque<Transaction>> CURRENT = new ThreadLocal<>(); private void registerTransaction(Transaction transaction) { if (CURRENT.get() == null) { CURRENT.set(new LinkedList<Transaction>()); } //将事务添加入本地线程事务队列的头部 CURRENT.get().push(transaction); }
注意:
在事务管理器TransactionManager.register注册事务,是将事务对象添加入本地线程事务队列的头部,后面获取事务时,也是从头部获取。(因为在TCC事务中,支持一个事务管理器管理多个事务transaction,而后创建的事务,要先提交,所以,入队和出队都要从头部)
©TransactionManager.propagationNewBegin传播并发起分支事务
注意调用时机:创建分支事务,在try阶段调用。
这里,首先创建分支事务,然后注册。
public Transaction propagationNewBegin(TransactionContext transactionContext) { //注意:这里创建分支BRANCH事务 Transaction transaction = new Transaction(transactionContext); //for performance tuning, at create stage do not persistent为了性能调优角度,在创建阶段,不持久化存储事务transaction // transactionRepository.create(transaction); //注册事务 registerTransaction(transaction); return transaction; }
此处区分于beginTransaction,这里创建事务对象为TransactionType.BRANCH分支事务、TransactionStatus.TRYING事务状态为try阶段。分支事务和ROOT根事务有同一个事务id。
public Transaction(TransactionContext transactionContext) { this.xid = transactionContext.getXid(); this.rootXid = transactionContext.getRootXid(); this.rootDomain = transactionContext.getRootDomain(); this.status = TransactionStatus.TRYING; this.transactionType = TransactionType.BRANCH; }
(d)TransactionManager.propagationExistBegin传播并获取分支事务
注意区别:
TransactionManager.propagationExistBegin:这里是侧重获取分支事务(需要从事务存储器中查找并获取事务);在confirm/cancle阶段调用
TransactionManager.propagationNewBegin是创建BRANCH事务,并存储,侧重于发起;在try阶段调用
public Transaction propagationExistBegin(TransactionContext transactionContext) throws NoExistedTransactionException { //根据事务上下文transactionContext.getXid(),从事务存储器transactionRepository查询事务 Transaction transaction = transactionRepository.findByXid(transactionContext.getXid()); if (transaction != null) { //将获取的事务,重新注册入TransactionManager中 registerTransaction(transaction); return transaction; } else { throw new NoExistedTransactionException(); } }
(e)TransactionManager.commit提交事务
public void commit(boolean asyncCommit) { //TAG1 获取当前事务队列的首个transaction final Transaction transaction = getCurrentTransaction(); //修改当前事务状态为CONFIRMING transaction.setStatus(TransactionStatus.CONFIRMING); //更新 transactionRepository.update(transaction); if (asyncCommit) { try { long statTime = System.currentTimeMillis(); asyncTerminatorExecutorService.submit(() -> commitTransaction(transaction)); logger.debug("async submit cost time:{}", System.currentTimeMillis() - statTime); } catch (Throwable commitException) { logger.warn("compensable transaction async submit confirm failed, recovery job will try to confirm later.", commitException.getCause()); //throw new ConfirmingException(commitException); } } else { commitTransaction(transaction); } } private void commitTransaction(Transaction transaction) { try { //提交事务 transaction.commit(); //完成commit后,从持久化存储中删除事务 transactionRepository.delete(transaction); } catch (Throwable commitException) { //如果commit提交失败,更新CONFIRMING状态的事务 try { transactionRepository.update(transaction); } catch (Exception e) { //ignore any exception here } logger.warn("compensable transaction confirm failed, recovery job will try to confirm later.", commitException); throw new ConfirmingException(commitException); } }
综上,transactionManager.commit逻辑如下:
1 先从当前事务管理器transactionManager获取线程变量的事务队列的首个事务; 2 修改状态为confirming; 3 存储器中更新事务; 4 transaction.commit();----如果commit失败,更新transaction(在提交的过程中,会更改transactionContext、参与者状态等信息,因此需要更新) 5 提交成功后,删除当前事务
TAG1 getCurrentTransaction 获取当前线程变量的事务队列的头部事务
public Transaction getCurrentTransaction() { if (isTransactionActive()) { return CURRENT.get().peek(); } return null; } //判断是否是活跃事务:只要当前线程变量的事务队列中有事务即可 public boolean isTransactionActive() { Deque<Transaction> transactions = CURRENT.get(); return transactions != null && !transactions.isEmpty(); }
(e)TransactionManager.rollback
public void rollback(boolean asyncRollback) { final Transaction transaction = getCurrentTransaction(); transaction.setStatus(TransactionStatus.CANCELLING); transactionRepository.update(transaction); if (asyncRollback) { try { asyncTerminatorExecutorService.submit(() -> rollbackTransaction(transaction)); } catch (Throwable rollbackException) { logger.warn("compensable transaction async rollback failed, recovery job will try to rollback later.", rollbackException); throw new CancellingException(rollbackException); } } else { rollbackTransaction(transaction); } } private void rollbackTransaction(Transaction transaction) { try { transaction.rollback(); transactionRepository.delete(transaction); } catch (Throwable rollbackException) { //try save updated transaction try { transactionRepository.update(transaction); } catch (Exception e) { //ignore any exception here } logger.warn("compensable transaction rollback failed, recovery job will try to rollback later.", rollbackException); throw new CancellingException(rollbackException); } }
逻辑和commit相同
(e)TransactionManager.enlistParticipant添加参与者到transaction
该方法在try阶段被调用。
在添加参与者Participant时,先加入参与者队列;然后根据transaction.getVersion()是否为0,判断是否需要执行存储、或者进行更新
public void enlistParticipant(Participant participant) { Transaction transaction = this.getCurrentTransaction(); transaction.enlistParticipant(participant); if (transaction.getVersion() == 0L) { // transaction.getVersion()为0,表示之前从未持久化存储过,需要调用进行存储 transactionRepository.create(transaction); } else { transactionRepository.update(transaction); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构