前言

看篇文章前请先查看如下文章,需要这些前提知识铺垫!
1. 分布式事务解决方案Seata入门介绍
2. 分布式事务解决方案Seata搭建
3. 分布式事务解决方案Seata实战-AT模式

分布式事务解决方案Seata入门介绍这篇文章中末尾做了TCC模式的应用场景,本文就不过多介绍了,接上文的理论知识,本文只演示Seata TCC模式实战!

环境:请参考分布式事务解决方案Seata搭建这篇文章,因为我这几篇文章都是连贯起来的!防止没必要的异常问题请根据往期文章搭建操作!

编码:请根据分布式事务解决方案Seata实战-AT模式这篇文章订单服务搭建,或者使用完整的测试demogithub地址

切换TCC模式

订单服不用改动,只需要改动库存服和账户服,配置什么的也不需要变更!
库存服
分布式事务解决方案Seata实战-TCC模式_其他

@LocalTCC
public interface StorageService {

    /**
     * 定义两阶段提交
     * name = 该tcc的bean名称,全局唯一
     * commitMethod = commit 为二阶段确认方法
     * rollbackMethod = rollback 为二阶段取消方法
     * BusinessActionContextParameter注解 可传递参数到二阶段方法
     * @param   -入参
     * @return String
     */
    // 扣减库存
    @TwoPhaseBusinessAction(name = "decrease", commitMethod = "commitTcc", rollbackMethod = "cancel")
    void decrease(@BusinessActionContextParameter(paramName = "productId")Long productId, @BusinessActionContextParameter(paramName = "count")Integer count);

    /**
     * 确认方法、可以另命名,但要保证与commitMethod一致
     * context可以传递try方法的参数
     *
     * @param context 上下文
     * @return boolean
     */
    boolean commitTcc(BusinessActionContext context);

    /**
     * 二阶段取消方法
     *
     * @param context 上下文
     * @return boolean
     */
    boolean cancel(BusinessActionContext context);
}

分布式事务解决方案Seata实战-TCC模式_回滚_02

@Slf4j
@Service
public class StorageServiceImpl implements StorageService {

    private static final Logger LOGGER = LoggerFactory.getLogger(StorageServiceImpl.class);

    @Resource
    private StorageDao storageDao;

    // 扣减库存
    @Override
    public void decrease(Long productId, Integer count) {
            LOGGER.info("------->storage-service中扣减库存开始");
            storageDao.decrease(productId, count);
            LOGGER.info("------->storage-service中扣减库存结束");
    }

    @Override
    public boolean commitTcc(BusinessActionContext context) {
        log.info("xid = " + context.getXid() + "扣减库存-提交成功");
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext context) {
        //todo 这里写中间件、非关系型数据库的回滚操作
        log.info("xid = " + context.getXid() + "回滚操作");
        log.info("please manually rollback this data-productId:" + context.getActionContext("productId"));
        log.info("please manually rollback this data-count:" + context.getActionContext("count"));
        return true;
    }
}

账户服
分布式事务解决方案Seata实战-TCC模式_解决方案_03

@LocalTCC
public interface AccountService {

    /**
     * 扣减账户余额
     */
    @TwoPhaseBusinessAction(name = "decrease", commitMethod = "commitTcc", rollbackMethod = "cancel")
    void decrease(@BusinessActionContextParameter(paramName = "userId")Long userId, @BusinessActionContextParameter(paramName = "money")BigDecimal money);

    /**
     * 确认方法、可以另命名,但要保证与commitMethod一致
     * context可以传递try方法的参数
     *
     * @param context 上下文
     * @return boolean
     */
    boolean commitTcc(BusinessActionContext context);

    /**
     * 二阶段取消方法
     *
     * @param context 上下文
     * @return boolean
     */
    boolean cancel(BusinessActionContext context);
}

分布式事务解决方案Seata实战-TCC模式_解决方案_04

/**
 * 账户业务实现类
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);

    @Resource
    AccountDao accountDao;
    /**
     * 扣减账户余额
     */
    @Override
    public void decrease(Long userId, BigDecimal money) {

        LOGGER.info("------->account-service中扣减账户余额开始");
        //try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        LOGGER.info("当前XID===>" + RootContext.getXID());
        accountDao.decrease(userId, money);
        LOGGER.info("------->account-service中扣减账户余额结束");
    }

    @Override
    public boolean commitTcc(BusinessActionContext context) {
        log.info("xid = " + context.getXid() + "扣减账户余额-提交成功");
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext context) {
        //todo 这里写中间件、非关系型数据库的回滚操作
        log.info("xid = " + context.getXid() + "回滚操作");
        log.info("please manually rollback this data-userId:" + context.getActionContext("userId"));
        log.info("please manually rollback this data-money:" + context.getActionContext("money"));
        return true;
    }
}

重启服务,无异常情况

库存服务
分布式事务解决方案Seata实战-TCC模式_回滚_05
账户服
分布式事务解决方案Seata实战-TCC模式_解决方案_06

重启服务,异常情况,在账户服,也就是调用链的末尾报错

账户服手动报错分布式事务解决方案Seata实战-TCC模式_ide_07
从新运行,发送请求测试
微服务调用者订单服!
分布式事务解决方案Seata实战-TCC模式_其他_08
订单服中并没有打出回滚操作,这里先不解释,看完库存和账户服在同一做解释!
库存服
分布式事务解决方案Seata实战-TCC模式_解决方案_09
账户服
分布式事务解决方案Seata实战-TCC模式_解决方案_10
订单服调用库存服和账户服,账户服报错了,账户服和库存服都执行了回滚操作,但是订单服并没有,这里可能有人会抬杠,说订单服没有实现TCC逻辑,这里订单服如果也加了TCC接口和实现的话那么整个TCC模式就不会产生作用,不信的话可以试试,那么这里报错了订单服如果要回滚怎么办呢,解决方案如下!

订单服实现回滚
订单服要实现回滚的话需要调整原有的调用顺序!
分布式事务解决方案Seata实战-TCC模式_其他_11
就是将调用订单服自己实现的操作放到其他调用服务的后面执行,如果服务提供者报错了,那么服务提供者那么那边会有回滚的方法处理,然后服务调用者根据服务提供者的调用返回的结果判断要不要执行!如果微服务调用者报错了,服务提供者也是能收到回滚操作通知的!这里可以将微服务调用者哪里的异常解开试一次!