概述
TCC 模式是一种侵入式的分布式事务解决方案,它不依赖于数据库的事务,而是要求开发者自定义完成 预提交、提交、回滚的方法逻辑。因此,它是一个种偏 复杂、灵活、有侵入性 的分布式事务处理方案。
Demo
这里附上seata的学习代码demo,开箱即用。包含AT/TCC/XA等模式的使用案例https://download.csdn.net/download/lmj3732018/88864802
使用
依赖与配置
Seata的TCC模式依赖和配置与AT模式完全一致,只是TCC模式不需要定义 undo_log 数据库表,这里不再赘述。
代码
1. 在使用时,我们需要在多个本地事务分支的外层使用 @GlobalTransactional 开启全局事务
@Override @GlobalTransactional(name="createOrder",rollbackFor=Exception.class) public Order saveOrder(OrderVo orderVo) { log.info("=============用户下单================="); log.info("当前 XID: {}", RootContext.getXID()); //获取全局唯一订单号 测试使用 Long orderId = UUIDGenerator.generateUUID(); //阶段一: 创建订单 Order order = orderService.prepareSaveOrder(orderVo,orderId); //扣减库存 storageFeignService.deduct(orderVo.getCommodityCode(), orderVo.getCount()); //扣减余额 accountFeignService.debit(orderVo.getUserId(), orderVo.getMoney()); return order; }
2. 定义各个本地事务分支的预提交、提交、回滚方法
OrderService 接口
/** * * 通过 @LocalTCC 这个注解,RM 初始化的时候会向 TC 注册一个分支事务。 */ @LocalTCC public interface OrderService { /** * TCC的try方法:保存订单信息,状态为支付中 * * 定义两阶段提交,在try阶段通过@TwoPhaseBusinessAction注解定义了分支事务的 resourceId,commit和 cancel 方法 * name = 该tcc的bean名称,全局唯一 * commitMethod = commit 为二阶段确认方法 * rollbackMethod = rollback 为二阶段取消方法 * BusinessActionContextParameter注解 传递参数到二阶段中 * useTCCFence seata1.5.1的新特性,用于解决TCC幂等,悬挂,空回滚问题,需增加日志表tcc_fence_log */ @TwoPhaseBusinessAction(name = "prepareSaveOrder", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true) Order prepareSaveOrder(OrderVo orderVo, @BusinessActionContextParameter(paramName = "orderId") Long orderId); /** * * TCC的confirm方法:订单状态改为支付成功 * * 二阶段确认方法可以另命名,但要保证与commitMethod一致 * context可以传递try方法的参数 * * @param actionContext * @return */ boolean commit(BusinessActionContext actionContext); /** * TCC的cancel方法:订单状态改为支付失败 * 二阶段取消方法可以另命名,但要保证与rollbackMethod一致 * * @param actionContext * @return */ boolean rollback(BusinessActionContext actionContext); }
OrderServiceImpl实现类
@Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Override @Transactional(rollbackFor = Exception.class) public Order prepareSaveOrder(OrderVo orderVo, @BusinessActionContextParameter(paramName = "orderId") Long orderId) { // 保存订单 Order order = new Order(); order.setId(orderId); order.setUserId(orderVo.getUserId()); order.setCommodityCode(orderVo.getCommodityCode()); order.setCount(orderVo.getCount()); order.setMoney(orderVo.getMoney()); order.setStatus(OrderStatus.INIT.getValue()); Integer saveOrderRecord = orderMapper.insert(order); log.info("保存订单{}", saveOrderRecord > 0 ? "成功" : "失败"); return order; } @Override public boolean commit(BusinessActionContext actionContext) { // 获取订单id long orderId = Long.parseLong(actionContext.getActionContext("orderId").toString()); //更新订单状态为支付成功 Integer updateOrderRecord = orderMapper.updateOrderStatus(orderId, OrderStatus.SUCCESS.getValue()); log.info("更新订单id:{} {}", orderId, updateOrderRecord > 0 ? "成功" : "失败"); return true; } @Override public boolean rollback(BusinessActionContext actionContext) { //获取订单id long orderId = Long.parseLong(actionContext.getActionContext("orderId").toString()); //更新订单状态为支付失败 Integer updateOrderRecord = orderMapper.updateOrderStatus(orderId, OrderStatus.FAIL.getValue()); log.info("更新订单id:{} {}", orderId, updateOrderRecord > 0 ? "成功" : "失败"); return true; } }
在 storageFeignService 及 accountFeignService的远程方法中也是以同样的方式定义上述三个方法。