前言
看篇文章前请先查看如下文章,需要这些前提知识铺垫!
1. 分布式事务解决方案Seata入门介绍
2. 分布式事务解决方案Seata搭建
3. 分布式事务解决方案Seata实战-AT模式
在分布式事务解决方案Seata入门介绍这篇文章中末尾做了TCC模式的应用场景,本文就不过多介绍了,接上文的理论知识,本文只演示Seata TCC模式实战!
环境:请参考分布式事务解决方案Seata搭建这篇文章,因为我这几篇文章都是连贯起来的!防止没必要的异常问题请根据往期文章搭建操作!
编码:请根据分布式事务解决方案Seata实战-AT模式这篇文章订单服务搭建,或者使用完整的测试demogithub地址
切换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);
}
@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;
}
}
账户服
@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);
}
/**
* 账户业务实现类
*/
@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;
}
}
重启服务,无异常情况
库存服务
账户服
重启服务,异常情况,在账户服,也就是调用链的末尾报错
账户服手动报错
从新运行,发送请求测试
微服务调用者订单服!
订单服中并没有打出回滚操作,这里先不解释,看完库存和账户服在同一做解释!
库存服
账户服
订单服调用库存服和账户服,账户服报错了,账户服和库存服都执行了回滚操作,但是订单服并没有,这里可能有人会抬杠,说订单服没有实现TCC逻辑,这里订单服如果也加了TCC接口和实现的话那么整个TCC模式就不会产生作用,不信的话可以试试,那么这里报错了订单服如果要回滚怎么办呢,解决方案如下!
订单服实现回滚
订单服要实现回滚的话需要调整原有的调用顺序!
就是将调用订单服自己实现的操作放到其他调用服务的后面执行,如果服务提供者报错了,那么服务提供者那么那边会有回滚的方法处理,然后服务调用者根据服务提供者的调用返回的结果判断要不要执行!如果微服务调用者报错了,服务提供者也是能收到回滚操作通知的!这里可以将微服务调用者哪里的异常解开试一次!