这是我参与更文挑战的第1天,活动详情查看: 更文挑战
总文档 :文章目录
Github : github.com/black-ant
一 . 前言
文章目的 :
- 基于 Feign 的 TCC-Transaction 案例
- 梳理 TCC-Transaction 的 主流程及 Debug 方向
TCC-Transaction 背景简介 :
TCC 模型
TCC 模型是完全依赖于业务处理的分布式事务模型 , 他将一个 [大事务] 通过代码逻辑分解为 [多个小事务] , TCC 模型同样是 2PC (两阶段提交) 的实现之一.
TCC 的操作可以分为3个阶段 : Try / Confirm / Cancel
- Try: 业务的主要处理 , 但仅进行初期操作 (例如订单生成)
- 尝试执行业务
- 所有子事务完成所有业务检查(一致性)
- 锁定资源 , 预留必须业务资源(准隔离性)
- Confirm: 对 Try 操作的一个补充,逐个执行Try操作指定的Confirm操作
- 真正执行业务,不作任何业务检查
- 只使用Try阶段预留的业务资源 , 扣除具体的资源
- Confirm 操作满足幂等性
- Cancel: 对Try操作的一个回撤
- 取消执行业务
- 释放Try阶段预留的业务资源, 业务上的回退
- Cancel操作满足幂等性
TCC 的优缺点
优点:
- 由业务方自行控制事务的范围
- 自如的控制数据库粒度处理 , 降低锁冲突
- 业务设计合理 , 可以大大提高吞吐量
- 代码配置简单 , 无需太多配置 , 集成方式便利
缺点 :
- 业务侵入大 , 耦合强 , 迁移及改造成本大
- 设计难度大
- 对于回滚的处理困难
- 为了满足一致性的要求,confirm和cancel接口必须实现幂等
二 . TCC-Transaction 案例
官方提供过一个基于 Dubbo 的处理案例 , 本案例是基于 Feign 进行 RestAPI 调用的 , 其核心原理其实是一致的.
为了后文分析时更加清楚 , 首先看一下案例源码 :
业务模块 :
- Order : 订单服务
- Capital : 账户服务
- RedPacket : 红包服务
订单支付后 , 扣除账户余额和红包 . 当红包余额不足时 , 发起回退
前期配置
TODO : TCC 的灵活配置
2.1 Order 服务
Order 主流程 : 发起整个事务及接口调用逻辑
- makePayment : 生成 Order 订单 , 调用红包及账户服务扣除余额
- confirmMakePayment
- cancelMakePayment : 确定后修改状态
1 | java复制代码@Service |
Capital , Red 远程调用
1 | java复制代码@Component |
2.2 Capital 账户处理余额
1 | java复制代码@Service |
2.3 RedPacket 红包处理
- record : 创建红包订单 , 扣除金额
- confirmRecord : 更新订单状态 , 扣除账户
- cancelRecord :
1 | java复制代码@Service |
2.4 流程总结
流程源码可以看 @项目源码
三 . 源码分析
3.1 TCC 成员梳理
- 事务 : ( Transaction )
- 事务ID对象 : ( TransactionXid )
- 事务状态对象 : ( TransactionStatus )
- 事务类型对象 : ( TransactionType )
- 参与者 : ( org.mengyun.tcctransaction.Participant )
- 事务管理器 : TransactionManager
- 事务恢复配置器 : RecoverConfig
- 事务恢复处理器 : TransactionRecovery
- 默认事务恢复配置实现 : DefaultRecoverConfig
- 事务恢复定时任务 : RecoverScheduledJob
- 事务恢复处理器 : TransactionRecovery
- 事务恢复处理器 : TransactionRecovery
3.2 TCC 流程快查
事务的主要流程如下所示 :
- 发起根事务 : MethodType.ROOT / begin / registerTransaction
- 传播发起分支事务 : MethodType.PROVIDER / try / propagationNewBegin
- 传播获取分支事务 : MethodType.PROVIDER / confirm / cancel / propagationExistBegin
- 提交事务 : commit / confirm / cancel /
- 回滚事务 : rollback / confirm / cancel /
- 添加事务 : enlistParticipant / try
- 事务拦截器 : @Compensable / @Aspect / @Transactional
3.3 流程一 : 切面的处理
TCC 的注解基于 @Aspect + @Compensable 实现切面的处理 , 核心的处理类为 CompensableTransactionAspect
1 | java复制代码@Aspect |
处理拦截器
当切面拦截后 , 此处会使用拦截器进行真正的具体逻辑 :
org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor
,可补偿事务拦截器。org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor
,资源协调者拦截器。
1 | java复制代码C05- CompensableTransactionInterceptor |
Pro 1 : Propagation 是什么 ?
Propagation 提供了多种传播方式 , 来定义具体的传播类型
1 | java复制代码public enum Propagation { |
Pro 2 : MethodType 是什么
MethodType 表示方法对应的事务类型 :
1 | java复制代码 |
M05_02_01 Transaction 实体类解析
3.4 事务的执行和通知
3.4.1 事务的commit
之前 C05- CompensableTransactionInterceptor # M05_02- rootMethodProceed 中通过 TransactionManager 执行 commit 操作
先看一下 TransactionManager 的结构 :
1 | java复制代码C09- TransactionManager |
Step 1 : TransactionManager 进行管理
TransactionManager 进行 Transaction 的配合和通过线程发起事务
1 | java复制代码 |
Step 2 :提交事务
1 | java复制代码// M09_06 此处提交事务 |
PS:C10_01 TransactionRepository 家族体系
1 | java复制代码public interface TransactionRepository { |
PS:C15_01 参数
Pro 1 : Xid 的对象
1 | java复制代码public interface Xid { |
3.4.2 事务的通知
事务的还有一个核心逻辑就是通知其他的应用执行相关的逻辑 , 那么事务是怎么相互告知的呢 ? 我们从实际应用出发 :
问题 :
疑点一 : 当 captial try 逻辑完成后 , 实际上已经返回了 , 并不会拿到对应的通知
现象 :
现象一 : 当 try 再次调用时 , 是通过 restAPI 接口进行网络调用 , 所以应该是外部调用实现的
1 | java复制代码// 找了相关的代码找到了这个类 : |
如果我们跟着相关的代码 , 会发现确实在出现rollbakc 时 , 会调用该参与者
所以我们能得出如下的结论 :
- 当出现异常后 , 会通过 Participant 调用相同的接口一次
- 调用对应接口后 , 会因为拦截器的原因 , 通过 Transaction 状态 , 调用对应的所属流程 (例如异常就是 rollback)
3.5 事务的回退
事务的回退时 , 会先调用起本身的 cancel 方法 ,其次会调用依赖微服务的原方法
PS : 确实是原方法 , 但是由于代理 , 会进入 CompensableTransactionAspect 切面
通过判断 TransactionContext 中的 status 决定执行什么方法
1 | java复制代码private Object providerMethodProceed(ProceedingJoinPoint pjp, TransactionContext transactionContext, boolean asyncConfirm, boolean asyncCancel) throws Throwable { |
3.6 事务的异步处理
TCC-Transaction 的处理默认是同步的 , 可以通过注解来配置异步处理
@Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = false, asyncCancel = false)
这里来看一下2者的区别 :
1 | java复制代码 public void rollback(boolean asyncRollback) { |
3.7 事务的恢复
事务的恢复和事务的通知并不是一个概念 , 当事务的初期执行出现异常后 , 事务在后续会通过定时任务的方式 , 完成事务的继续执行操作
- org.mengyun.tcctransaction.recover.RecoverConfig,事务恢复配置接口
- org.mengyun.tcctransaction.spring.recover.DefaultRecoverConfig,默认事务恢复配置实现
- org.mengyun.tcctransaction.spring.recover.RecoverScheduledJob,事务恢复定时任务,基于 Quartz 实现调度,不断不断不断执行事务恢复
总结
正常处理流程 , 执行 Confirm
1 | java复制代码A- 事务发起者 (订单服务) |
回退处理逻辑 , 执行 Cancel
1 | java复制代码// PS : 前几步都是一样的 , 基于代理的方式 |
@Compensable 的使用
1 | java复制代码// 整个流程中共有以下几个地方需要标注 @Compensable , 我们来单独看看其中的关联 |
感谢和参考
本文转载自: 掘金