TCC服务设计和实现注意事项

TCC是成熟的分布式事务解决方案,可解决跨库操作的数据一致性问题。实现TCC服务时,要注意幂等控制、允许空回滚、防资源悬挂、业务数据可见性和并发访问控制。蚂蚁金服推出FMT和XA模式简化接入,三种模式互补,SOFA - DTX已应用于多个项目。

转载自:https://blog.youkuaiyun.com/bntX2jSQfEHy7/article/details/81058746

  

一、TCC简介

  TCC是一种比较成熟的分布式事务解决方案,可用于解决跨库操作的数据一致性问题;TCC是服务化的两阶段编程模型,其Try、Confirm、Cancel 3个方法均由业务编码实现;
  其中Try操作作为一阶段,负责资源的检查和预留,Confirm操作作为二阶段提交操作,执行真正的业务,Cancel是预留资源的取消;

  如下图所示,业务实现TCC服务之后,该TCC服务将作为分布式事务的其中一个资源,参与到整个分布式事务中;事务管理器分2阶段协调TCC服务,在第一阶段调用所有TCC服务的Try方法,在第二阶段执行所有TCC服务的Confirm或者Cancel方法;

二、用户在实现TCC服务时,有以下注意事项:

1、幂等控制

  无论是网络数据包重传,还是异常事务的补偿执行,都会导致TCC服务的Try、Confirm或者Cancel操作被重复执行;用户在实现TCC服务时,需要考虑幂等控制,即Try、Confirm、Cancel 执行次和执行多次的业务结果是一样的。如果不能保证幂等性,则会产生严重的问题,造成资源的重复使用或者重复释放,进而导致业务故障。

  应对策略:
  对于幂等类型的问题,通常的手段是引入幂等字段进行防重放攻击。对于分布式事务框架中的幂等问题,可以通过增加一张事务状态控制表来实现。这个表会记录每个分布式事务的状态:INIT - 初始化、CONFIRMED - 已提交、ROLLBACKED - 已回滚。幂等记录的插入时机是参与者的Try方法,此时的分支事务状态会被初始化为INIT。然后当二阶段的Confirm/Cancel执行时会将其状态置为CONFIRMED/ROLLBACKED。事务操作时,通过对幂等记录判断之后再执行。

2、允许空回滚

  如下图所示,事务协调器在调用TCC服务的一阶段Try操作时,可能会出现因为丢包而导致的网络超时,此时事务协调器会触发二阶段回滚,调用TCC服务的Cancel操作;

  即TCC服务在未收到Try请求的情况下收到Cancel请求,这种场景被称为空回滚;TCC服务在实现时应当允许空回滚的执行;

  应对策略:
  同样基于事务状态控制表,当Try方法被成功执行后,会插入一条记录,标识该分支事务处于INIT状态。所以后续当二阶段的Cancel方法被调用时,可以通过查询控制表的对应记录进行判断。如果记录存在且状态为INIT,就表示一阶段已成功执行,可以正常执行回滚操作,释放预留的资源;如果记录不存在则表示一阶段未执行,本次为空回滚,不释放任何资源。

3、防资源悬挂

  如下图所示,事务协调器在调用TCC服务的一阶段Try操作时,可能会出现因网络拥堵而导致的超时,此时事务协调器会触发二阶段回滚,调用TCC服务的Cancel操作;在此之后,拥堵在网络上的一阶段Try数据包被TCC服务收到,出现了二阶段Cancel请求比一阶段Try请求先执行的情况,此时如果执行Try操作进行资源预留就会形成资源悬挂。

  资源悬挂的本质原因在于,一阶段和二阶段的执行顺序没有被严格地保证。所以相应的解决方案还是通过读取事务状态控制表的事务状态。由于悬挂的产生背景是一阶段方法根本就未执行,所以此时事务控制记录是不存在的,需要在二阶段中处理ROLLBACK的情况(因为超时后触发回滚不可能存在二阶段为CONFIRM)。
  处理方案为在判断为空回滚的场景下(体现在对应一阶段事务控制记录不存在),插入一条状态为ROLLBACKED的控制记录。那么下次当一阶段Try抵达执行的时候,首先会尝试插入状态为INIT的事务控制记录。如果插入失败,表示当前分支事务的记录已经存在,Try无需继续执行。

4、业务数据可见性控制;

  TCC服务的一阶段Try操作会做资源的预留,在二阶段操作执行之前,如果其他事务需要读取被预留的资源数据,那么处于中间状态的业务数据该如何向用户展示,需要业务在实现时考虑清楚;通常的设计原则是“宁可不展示、少展示,也不多展示、错展示”;

5、业务数据并发访问控制;

  TCC服务的一阶段Try操作预留资源之后,在二阶段操作执行之前,预留的资源都不会被释放;如果此时其他分布式事务修改这些业务资源,会出现分布式事务的并发问题;
  用户在实现TCC服务时,需要考虑业务数据的并发控制,尽量将逻辑锁粒度降到最低,以最大限度的提高分布式事务的并发性;

三、总结

  蚂蚁金服大部分业务系统均采用TCC的方式接入分布式事务,但设计TCC服务时要遵循大量设计规范,这无疑对用户提了非常高的要求;为了简化用户接入分布式事务的门槛,蚂蚁金服的分布式事务框架(SOFA-DTX)推出了FMT(Framework-managed transactions)模式和XA模式,这两种模式均不需要用户实现TCC服务,用户只需要关注自身业务SQL便可;DTX的三种模式:TCC、FMT和XA相互之间是功能互补,相辅相成的,形成了蚂蚁金服完善的分布式事务解决方案。
  SOFA-DTX全面覆盖金融场景,金融级容灾保障、提供丰富的接入模式并且使用简洁易于接入;目前已经应用在支付宝、网上银行、蚂蚁财富、芝麻信用、南京银行等项目中。

### TCC与AT事务模式混用的实现方案 #### 方案概述 TCC(Try-Confirm-Cancel) AT(Automatic Transaction)是分布式事务中的两种常见模式。两者可以混合使用来解决复杂的业务场景需求。以下是具体的实现方式及其注意事项。 --- #### 1. **基础架构支持** 为了使 TCC AT 模式能够协同工作,需要统一的事务协调器 TC(Transaction Coordinator)。TC 是整个分布式事务的核心组件,负责管理调度全局事务以及分支事务的状态转换[^2]。 通过引入 SDK 支持,例如 `<groupId>cn.gov.zcy.boot</groupId>` 提供的 `spring-boot-starter-ztx` 工具包,可以在项目中轻松集成这两种模式的支持功能[^3]。 --- #### 2. **具体实现步骤** ##### (1)配置全局事务管理器 TM TM 定义了全局事务的作用域,并根据 TC 的反馈决定提交或回滚全局事务。在 Spring Boot 中可以通过注解 `@GlobalTransactional` 来标记服务入口方法作为全局事务的起点[^2]。 ```java @Service public class OrderService { @Autowired private InventoryClient inventoryClient; @GlobalTransactional(timeoutMills = 60000, name = "order-create-tx") public void createOrder(Order order) { // 创建订单逻辑... // 调用库存扣减操作(可能涉及 TCC) inventoryClient.deductInventory(order.getProductId(), order.getQuantity()); } } ``` ##### (2)定义资源管理器 RM 对于不同的业务模块,可以选择适合的事务模式: - 对于简单的数据修改操作(如更新数据库记录),可以直接采用 AT 模式。 - 需要复杂补偿机制的操作,则需设计TCC 模式。 以库存管理系统为例,在创建冻结表的基础上完成 TCC 方法的设计[^1]: ```java // 库存服务接口 public interface InventoryTccService { boolean tryDeduct(String productId, int quantity); void confirmDeduction(String productId, int quantity); void cancelDeduction(String productId, int quantity); } // 实现类 @Component public class InventoryTccServiceImpl implements InventoryTccService { @Override public boolean tryDeduct(String productId, int quantity) { // 尝试锁定库存并写入冻结表 return insertFreezeRecord(productId, quantity); } @Override public void confirmDeduction(String productId, int quantity) { // 扣除实际库存并删除冻结记录 updateStockAndRemoveFreeze(productId, quantity); } @Override public void cancelDeduction(String productId, int quantity) { // 删除冻结记录恢复原状 removeFreezeRecord(productId, quantity); } } ``` ##### (3)组合调用 在一个全局事务范围内,既可以包含基于 AT 模式的简单 SQL 更新,也可以嵌套调用 TCC 接口完成更复杂的业务流程。关键在于确保所有参与方均能正确响应 TC 发起的一阶段准备二阶段提交/回滚指令[^4]。 --- #### 3. **注意事项** - **一致性保障**:无论选用哪种模式,都需要严格遵循两阶段提交协议的要求。特别是当多个异构系统共同参与到同一个全局事务时,应特别注意各环节之间的依赖关系及失败重试策略[^4]。 - **性能优化**:相比单纯的 AT 模式,TCC 增加了一定程度上的开发成本技术难度。因此建议仅针对那些确实无法通过标准 ORM 自动化处理的情况才启用 TCC[^1]。 - **异常捕获**:由于网络延迟或其他不可控因素可能导致部分节点未能及时收到最终决策通知,所以必须做好超时检测与手动干预预案。 --- ### 示例代码片段 以下是一个典型的混合使用案例演示如何在同一笔交易里同时运用到 AT TCC 功能: ```java @Transactional(rollbackFor = Exception.class) public void placeOrder(Long userId, Long goodsId, Integer count) throws BizException { // Step 1: 使用 AT 模式保存订单基本信息至本地 DB Orders orders = new Orders(); orders.setUserId(userId); orders.setStatus("PENDING"); saveOrders(orders); // Step 2: 调用远程库存服务执行 TCC 流程 if (!inventoryClient.tryDeduct(goodsId, count)) { throw new BizException("Insufficient stock!"); } // 如果后续其他条件不满足则触发取消路径 if (someConditionNotMet()) { inventoryClient.cancelDeduction(goodsId, count); throw new BizException("Order placement failed due to condition check."); } // 正常结束时确认扣除动作 inventoryClient.confirmDeduction(goodsId, count); // 更改订单状态标志成功 orders.setStatus("SUCCESSFUL"); updateOrdersStatus(orders.getId(), "SUCCESSFUL"); } ``` --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值