Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
TCC 与 Seata AT 事务一样都是两阶段事务,它与 AT 事务的主要区别为:
TCC 对业务代码侵入严重:每个阶段的数据操作都要自己进行编码来实现,事务框架无法自动处理。
TCC 效率更高:不必对数据加全局锁,允许多个事务同时操作数据。
接下来分析下Seata 如何使用TCC 模式。
案例需求:账户服务调用订单和库存服务进行下单操作,保证某个环节异常时,事务能实现全局一致。
参考Seata 专栏搭建三个测试模块,注意这里不需要undo_log 表。
当前主要组件版本为:
spring.boot:2.3.12.RELEASE
spring.cloud:Hoxton.SR12
spring.cloud.alibaba:2.2.7.RELEASE
Nacos : 2.0.3
Seata: 1.4.2
mybatis-plus: 3.4.2
TCC 模式,不依赖于底层数据资源的事务支持:
因为TCC 需要自定义分支事务处理逻辑,所以我们需要编写一个账户购买商品服务接口,并添加相关注解。
@LocalTCC
public interface AccountTblService extends IService<AccountTbl> {
/**
* 执行资源检查及预留操作
*/
@TwoPhaseBusinessAction(name = "prepareBuy", commitMethod = "commit", rollbackMethod = "rollback")
Object prepareBuy(@BusinessActionContextParameter(paramName = "userId") String userId, String code, @BusinessActionContextParameter(paramName = "count") Long count);
/**
* 全局事物进行提交
*/
boolean commit(BusinessActionContext actionContext);
/**
* 全局事务进行回滚
*/
boolean rollback(BusinessActionContext actionContext);
}
相关注解说明:
编写接口实现类:
@Service
@Slf4j
public class AccountTblServiceImpl extends ServiceImpl<AccountTblMapper, AccountTbl> implements AccountTblService {
@Autowired
AccountTblMapper accountTblMapper;
@Autowired
OrderClint orderClint;
@Autowired
StorageApi storageApi;
@Override
public Object prepareBuy(String userId, String code, Long count) {
log.info("开始TCC xid:" + RootContext.getXID());
//1.查询账户 扣款
AccountTbl accountTbl = accountTblMapper.selectById(userId);
AccountTbl accountTbl1 = accountTbl.setMoney(accountTbl.getMoney() - count);
accountTblMapper.updateById(accountTbl1);
//2.远程创建订单
orderClint.insertOrder(accountTbl.getUserId(), code, count);
//3.远程扣库存
storageApi.tcc("iphone11", count);
return "执行完毕!";
}
@Override
public boolean commit(BusinessActionContext actionContext) {
log.info("xid = " + actionContext.getXid() + "提交成功");
return true;
}
@Override
public boolean rollback(BusinessActionContext actionContext) {
// 获取下单时的提交参数
String userId = actionContext.getActionContext("userId").toString();
Long count = Long.parseLong(actionContext.getActionContext("count").toString());
// 进行分支事务扣掉的金额回滚
AccountTbl accountTbl = accountTblMapper.selectById(userId);
AccountTbl accountTbl1 = accountTbl.setMoney(accountTbl.getMoney() + count);
accountTblMapper.updateById(accountTbl1);
log.info("xid = " + actionContext.getXid() + "进行回滚操作");
return true;
}
}
使用@GlobalTransactional开启全局事务:
@GetMapping("/testTcc")
@GlobalTransactional
public Object test() {
return accountTblService.prepareBuy("11111111","iphone11",1L);
}
订单和库存服务也仿照上面书写。
发生异常时,可以看到,TCC模式进行了回滚操作。
上面简单实现了TCC 模式,但是存在很多问题,使用起来也巨麻烦,所以就不再深究了