tcc分布式事务_分布式事务解决方案之TCC
业务场景介绍
public class OrderService { // 库存服务 @Autowired private InventoryService inventoryService; // 积分服务 @Autowired private CreditService creditService; // 仓储服务 @Autowired private WmsService wmsService; // 对这个订单完成支付 public void pay(){ //对本地的的订单数据库修改订单状态为"已支付" orderDAO.updateStatus(OrderStatus.PAYED); //调用库存服务扣减库存 inventoryService.reduceStock(); //调用积分服务增加积分 creditService.addCredit(); //调用仓储服务通知发货 wmsService.saleDelivery(); }}
TCC 实现阶段一:Try
TCC 实现阶段二:Confirm
TCC 实现阶段三:Cancel
总结与思考
你要玩 TCC 分布式事务的话:首先需要选择某种 TCC 分布式事务框架,各个服务里就会有这个 TCC 分布式事务框架在运行。然后你原本的一个接口,要改造为 3 个逻辑,Try-Confirm-Cancel:
- 先是服务调用链路依次执行 Try 逻辑。
- 如果都正常的话,TCC 分布式事务框架推进执行 Confirm 逻辑,完成整个事务。
- 如果某个服务的 Try 逻辑有问题,TCC 分布式事务框架感知到之后就会推进执行各个服务的 Cancel 逻辑,撤销之前执行的各种操作。
万一某个服务的 Cancel 或者 Confirm 逻辑执行一直失败怎么办呢?
TCC 事务框架会通过活动日志记录各个服务的状态。举个例子,比如发现某个服务的 Cancel 或者 Confirm 一直没成功,会不停的重试调用它的 Cancel 或者 Confirm 逻辑,务必要它成功!
最后,来看看给我们的业务,加上分布式事务之后的整个执行流程:
TCC 分布式事务框架:ByteTCC,TCC-transaction,Himly。
异常场景及应对机制
幂等处理
因为网络抖动等原因,分布式事务框架可能会重复调用同一个分布式事务中的一个分支事务的二阶段接口。所以分支事务的二阶段接口Confirm/Cancel需要能够保证幂等性。如果二阶段接口不能保证幂等性,则会产生严重的问题,造成资源的重复使用或者重复释放,进而导致业务故障。
空回滚
先来说定义,当没有调用参与方Try方法的情况下,就调用了二阶段的Cancel方法,Cancel方法需要有办法识别出此时Try有没有执行。如果Try还没执行,表示这个Cancel操作是无效的,即本次Cancel属于空回滚;如果Try已经执行,那么执行的是正常的回滚逻辑。
Try方法没有执行成功,然而此时这笔分布式事务和分支事务已经落库。有两种情况会触发分布式事务的回滚:
1、发起方认为当前分布式事务无法成功,主动通知TC事务协调者回滚
2、TC事务协调者发现分布式事务超时,被动触发回滚
资源悬挂
- 发起方通过RPC调用参与者一阶段Try,但是发生网络阻塞导致RPC超时。
- RPC超时后,TC会回滚分布式事务(可能是发起方主动通知TC回滚或者是TC发现事务超时后回滚),调用已注册的各个参与方的二阶段Cancel。
- 参与方空回滚后,发起方对参与者的一阶段Try才开始执行,进行资源预留从而形成悬挂。
三种异常总结
前面讨论了TCC分布式事务三种典型的异常类型,它们的解决方案都依赖于一张事务状态控制表。我们来尝试总结一下它们各自的特点。
幂等
问题:TC重复调用二阶段
解决:事务状态控制记录作为控制手段,只有存在INIT记录时才执行,存在CONFIRMED/ROLLBACKED记录时不再执行
空回滚
问题:TC回滚事务调用二阶段,但一阶段尚未执行
解决:事务状态控制记录作为控制手段,无记录时即为空回滚
资源悬挂
问题:TC回滚事务调用二阶段完成空回滚后,一阶段执行成功
解决:事务状态控制记录作为控制手段,二阶段发现无记录时插入记录,一阶段执行时检查记录是否存在
共通点
- 核心的解决方案就是事务状态控制表
- 幂等控制作为最基础的异常处理手段;资源悬挂的前置条件是空回滚,所以发生空回滚时会插入一条状态为ROLLBACKED的控制记录。
还没有评论,来说两句吧...