实战!阿里神器 Seata 实现 TCC模式 解决分布式事务,真香(五)
2、confirm方法
confirm方法
①处的代码从幂等工具类中根据当前类和全局事务ID获取值,由于try阶段执行成功会向其中添加值,confirm方法执行成功会移出这个值,因此在confirm开头判断这个值是否存在就起到了幂等效果,防止重试的效果。
⑥处的代码从幂等工具类中移出try方法中添加的值。
②处的代码是从BusinessActionContext中获取try方法中的入参。
③处的代码是释放掉冻结的库存
④处的代码是修改订单的状态为已完成。
“注意:1. 开启本地事务 2. 注意返回值,返回false时将会重试”
3、cancel方法
cancel方法
①处的代码是向事务日志记录表中插入一条数据,标记当前事务进入cancel方法,用来防止悬挂,这个和try方法中的①处的代码相呼应。
②处的代码是为了防止幂等和空回滚,因为只有当try方法中执行成功幂等工具类中对应的当前类和全局事务ID才会存储该值。这样既防止了幂等,也防止了空回滚。
③处的代码恢复冻结的库存。
④处的代码删除这笔订单
⑤处的代码是移出幂等工具类当前类和全局事务ID对应的值。
3、如何防止TCC模型的三个异常?
实现方法有很多,有些案例是全部使用事务日志表记录当前的状态,这样完美的解决了幂等、空回滚、悬挂的问题。
陈某这里为了方便,使用了两种方案,如下:
1、幂等、空回滚
使用了一个幂等工具类,其中是个Map,key为当前类和全局事务ID,value是时间戳。
代码如下:
思路如下:
- 在try方法最后使用幂等工具类中的add方法添加值
- 在confirm、cancel方法中使用幂等工具类中的remove方法移出值
- 在confirm、cancel方法中使用幂等工具类中get方法获取值,如果为空,则表示已经执行过了,直接返回true,这样既防止了幂等,也防止了空回滚。
2、悬挂
悬挂的实现依靠的是事务日志表,表结构如下:
CREATE TABLE `transactional_record` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`xid` varchar(100) NOT NULL,
`status` int(1) DEFAULT NULL COMMENT '1. try 2 commit 3 cancel ',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
其中的xid是全局事务ID,status是事务的状态。
“其他的字段自己可以扩展”
解决悬挂问题的逻辑如下:
- cancel方法中将当前全局事务ID记录到事务日志表中,状态为cancel
- try方法执行资源操作前检查事务日志表中当前全局事务ID是否已经是cancel状态
4、创建订单的业务方法
上面只是完成了TCC的三个方法,主业务事务发起方还未提供,代码如下:
@GlobalTransactional这个注解开启了全局事务,是事务的发起方。
内部直接调用的TCC的try方法。
5、其他的配置
以上只是列出了关键的步骤,剩余其他的配置自己根据案例源码完善,如下:
- 接口测试
- 整合nacos
- 整合feign
- 整合seata,TCC模式中的配置和AT模式的Seata配置相同
“注意:一定要配置Seata的事务组tx-service-group,配置方法见之前的文章。”
6、总结
TCC事务模型相对来说比较简单的一种,有兴趣的可以下载源码试试。
文章转自公众号:码猿技术专栏