Seata源码——TCC模式使用01

什么是TCC

TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),他们的具体含义如下:
Try:对业务资源的检查并预留。
Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功。
Cancel:对业务处理进行取消,即回滚操作,该步骤回对 Try 预留的资源进行释放。
TCC 是一种侵入式的分布式事务解决方案,以上三个操作都需要业务系统自行实现,对业务系统有着非常大的入侵性,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。
请添加图片描述

Seata官网介绍

https://seata.io/zh-cn/docs/dev/mode/tcc-mode

特点

侵入性比较强,并且需要自己实现相关事务控制逻辑。
在整个过程基本没有锁,性能较强。

案例

order 服务

Order服务同时担任TM和RM角色。
TM角色下@GlobalTransactional负责管理全局事务。

 @GlobalTransactional //开启全局事务
    @Override
    public void create(Order order) {
    orderService.prepareCreateOrder(null,
                order.getId(),
                order.getUserId(),
                order.getProductId(),
                order.getCount(),
                order.getMoney());
}

Seata 实现 TCC 操作需要定义一个接口,在接口中添加以下方法:
Try - prepareCreateOrder() --方法的名称可以根据实际业务指定
Confirm - commit()
Cancel - rollback()

RM角色下LocalTccAction被@LocalTCC+@TwoPhaseBusinessAction标注,作为一个TCC资源向TC注册,管理分支事务注册、提交和回滚

@LocalTCC
public interface OrderService {

    /*
    第一阶段的方法
    通过注解指定第二阶段的两个方法名

    BusinessActionContext 上下文对象,用来在两个阶段之间传递数据
    @BusinessActionContextParameter 注解的参数数据会被存入 BusinessActionContext
     */
    @TwoPhaseBusinessAction(name = "orderTccService", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepareCreateOrder(BusinessActionContext businessActionContext,
       @BusinessActionContextParameter(paramName = "orderId") Long orderId,
       @BusinessActionContextParameter(paramName = "userId") Long userId,
       @BusinessActionContextParameter(paramName = "productId") Long productId,
       @BusinessActionContextParameter(paramName = "count") Integer count,
       @BusinessActionContextParameter(paramName = "money") BigDecimal money);

    // 第二阶段 - 提交
    boolean commit(BusinessActionContext businessActionContext);

    // 第二阶段 - 回滚
    boolean rollback(BusinessActionContext businessActionContext);

}
@Component
@Slf4j
public class OrderTccServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Transactional
    @Override
    public boolean prepareCreateOrder(BusinessActionContext businessActionContext, Long orderId, Long userId, Long productId, Integer count, BigDecimal money) {
        //插入订单 设置状态为0 --冻结
        orderMapper.create(new Order(orderId,userId,productId,count,money,0));
        log.info("创建订单第一阶段 冻结订单成功");
        //第一阶段成功 添加一个标识
        ResultHolder.setResult(OrderTccAction.class,businessActionContext.getXid(),"p");
        return true;
    }
    @Transactional
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
       //判断第一阶段的成功标记,没有标记则不执行提交操作
        if (ResultHolder.getResult(OrderTccAction.class,businessActionContext.getXid())==null){
            return true;
        }


        //修改订单状态 0---》1 正常状态的转变
        //通过订单id
        Long orderId = Long.parseLong(businessActionContext.getActionContext("orderId").toString());
        orderMapper.updateStatus(orderId,1);
        log.info("创建订单第二阶段 提交订单 解冻成功");

        //删除标识 防止一直重复提交
        ResultHolder.removeResult(OrderTccAction.class,businessActionContext.getXid());

        return true;
    }
    @Transactional
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        //第一阶段没有完成的情况下,不必执行回滚
        //因为第一阶段有本地事务,事务失败时已经进行了回滚。
        //如果这里第一阶段成功,而其他全局事务参与者失败,这里会执行回滚
        //幂等性控制:如果重复执行回滚则直接返回
        log.info("创建 order 第二阶段回滚,删除订单 - "+businessActionContext.getXid());
        //判断第一阶段的成功标记,没有标记则不执行提交操作
        if (ResultHolder.getResult(OrderTccAction.class,businessActionContext.getXid())==null){
            return true;
        }

        //通过订单id
        Long orderId = Long.parseLong(businessActionContext.getActionContext("orderId").toString());
        orderMapper.deleteById(orderId);
        log.info("创建订单第二阶段 回滚订单 删除成功");
        //删除标识 防止一直重复提交
        ResultHolder.removeResult(OrderTccAction.class,businessActionContext.getXid());

        return true;
    }
}

库存服务

Account库存服务是纯粹的一个RM,负责管理分支事务的提交和回滚。

    @Override
    public void decrease(Long userId, BigDecimal money) {
        //调用TCC 第一阶段的方法
        accountTccService.prepareDecreaseAccount(null,userId,money);
    }
@LocalTCC
public interface AccountTccService {
    @TwoPhaseBusinessAction(name = "accountTccService ")
    boolean prepareDecreaseAccount(BusinessActionContext businessActionContext,
               @BusinessActionContextParameter(paramName = "userId") Long userId,
               @BusinessActionContextParameter(paramName = "money")BigDecimal money

               );

    boolean commit(BusinessActionContext businessActionContext);

    boolean rollback(BusinessActionContext businessActionContext);

}
@Component
@Slf4j
public class AccountTccServiceImpl implements AccountTccService {
    @Autowired
    private AccountMapper accountMapper;
    @Transactional
    @Override
    public boolean prepareDecreaseAccount(BusinessActionContext businessActionContext, Long userId, BigDecimal money) {
        log.info("扣减金额第一阶段 ,开始执行冻结金额操作");
        Account account = accountMapper.selectById(userId);
        if (account.getResidue().compareTo(money)<0){
            throw  new  RuntimeException("可用金额不足,金额冻结失败");
        }
        //执行冻结操作
        accountMapper.updateFrozen(userId,account.getResidue().subtract(money),account.getFrozen().add(money));
        if (Math.random()<0.5){
            throw new RuntimeException("模拟异常");
        }

        //创建标识
        ResultHolder.setResult(getClass(),businessActionContext.getXid(),"p");
        log.info("扣减金额第一阶段 ,执行冻结金额完成");
        return false;
    }
    @Transactional
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        log.info("扣减金额第二阶段 ,开始执行提交操作");
        //防止重复提交
        if (ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return  true;
        }

        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());

        BigDecimal money = new BigDecimal(businessActionContext.getActionContext("money").toString());


        accountMapper.updateFrozenToUsed(userId,money);
        //删除标识
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return false;
    }
    @Transactional
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        log.info("扣减金额第二阶段 ,开始执行回滚操作");
        //防止重复回滚
        if (ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return  true;
        }
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());

        BigDecimal money = new BigDecimal(businessActionContext.getActionContext("money").toString());

        accountMapper.updateFrozenToResidue(userId,money);
        //删除标识
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        log.info("扣减金额第二阶段 ,执行回滚操作完成");
        return false;
    }
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用FeignClient调用TCC模式下分布式事务的示例: 1. 首先,在pom.xml文件中添加Feign和Seata的依赖: ```xml <dependencies> <!-- Seata --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.6.1</version> </dependency> <!-- Feign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.2.RELEASE</version> </dependency> </dependencies> ``` 2. 在启动类上添加@EnableFeignClients注解启用FeignClient: ```java @SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 创建FeignClient接口,并使用@LocalTCC注解标记TCC模式下的方法: ```java @FeignClient(name = "order-service") public interface OrderServiceClient { @RequestMapping(value = "/order/create", method = RequestMethod.POST) @LocalTCC Boolean createOrder(Order order); } ``` 4. 在业务逻辑中调用FeignClient: ```java @Service public class BusinessService { @Autowired private OrderServiceClient orderServiceClient; @GlobalTransactional public void createOrder(Order order) { orderServiceClient.createOrder(order); } } ``` 在上述代码中,@GlobalTransactional注解表示开启了一个全局事务,如果业务逻辑执行失败,Seata框架会回滚之前的所有操作。同时,业务逻辑中调用了FeignClient的createOrder方法,该方法被@LocalTCC注解标记,表示使用TCC模式处理分布式事务。 上述示例中的OrderServiceClient是一个FeignClient接口,其调用的服务名为order-service,对应的服务需要在Seata中注册为一个TC服务,同时需要在该服务的配置文件中添加Seata的相关配置,以支持TCC模式下的分布式事务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值