接着前面两篇说,下面我们继续对 Seata 的 TCC 模式进行讨论。
TCC
原理回顾
简单回顾一下TCC的原理 参考 蚂蚁金服的博客
正常事务逻辑
- try
- cancel 或 confirm
![746a96ff2e915c6a01d48fa39b3bd87b.png](https://i-blog.csdnimg.cn/blog_migrate/261f8080b1dfd9aa8ceaadedac5f1ddc.jpeg)
允许空回滚
1 未正常 try
2 执行了空 cancel
![3634f998cb1bc10ccb80df054d5bdda7.png](https://i-blog.csdnimg.cn/blog_migrate/c98c10d5681e6aa4032091e7a0c27f34.jpeg)
TCC 服务在未收到 Try 请求的情况下收到 Cancel 请求,这种场景被称为空回滚;空回滚在生产环境经常出现,用户在实现TCC服务时,应允许允许空回滚的执行,即收到空回滚时返回成功。
防悬挂控制
- try超时
- cancel成功
- try重试
- Confirm 或者 Cancel 永远不会得到执行,造成悬挂。
![f06160b7ca992fd7a752501dc9fd628d.png](https://i-blog.csdnimg.cn/blog_migrate/a79a973f4e77530691d96ffd1a6244b9.jpeg)
此外,除了上面这些,和AT一样,还是要注意幂等的控制。
代码实现
先讲下抽象流程和注意事项
![06e591e146e93b5f7cad59a3cc658c91.png](https://i-blog.csdnimg.cn/blog_migrate/797c27fbb4d13860ff28157a000800ec.jpeg)
- 首先定义事务接口,接口中就是你的tcc三个方法,对应代码中的prepare、commit、rollback。
- 注意加@LocalTCC 注解(必要),适用于SpringCloud+Feign模式下的TCC
- @TwoPhaseBusinessAction(必要) 注解try方法,name 一般写方法名就行,注意全局唯一,commitMethod对应提交方法名,rollbackMethod对应回滚方法名。
- BusinessActionContext 就是 seata tcc 的事务上下文,用于存放 tcc 事务的一些关键数据。BusinessActionContext 对象可以直接作为 commit 方法和 rollbakc 方法的参数,Seata 会自动注入参数:
![2f312beac90c586822794a0cbab78947.png](https://i-blog.csdnimg.cn/blog_migrate/56fabe5274296655166a36a12c7b019f.jpeg)
- @BusinessActionContextParameter
该注解用来修饰 Try 方法的入参,被修饰的入参可以在 Commit 方法和 Rollback 方法中通过 BusinessActionContext 获取
![dd730605b66c97de451b09f656575652.png](https://i-blog.csdnimg.cn/blog_migrate/eb0669e9e33420c1e8bae5d2793648f1.jpeg)
我们根据 官方的例子用一个业务场景串一下。
这是一个转账的操作:
接口定义:
![a1978e4da29ffc3730424933468855c8.png](https://i-blog.csdnimg.cn/blog_migrate/25ec5d43ccc2a898d68e38fbf923ab87.jpeg)
在事务调用入口加入 @GlobalTransactional
![32da671df201bec42c1218c5f6cc756e.png](https://i-blog.csdnimg.cn/blog_migrate/6c3618acf4891d03d18880cf7f45115a.jpeg)
- 先让扣钱参与者准备扣钱,如果失败,则回滚本地和分布式事务
看下扣钱的try方法实现:
![ab46e48da3ed836b9aa02ec9ffeea3b9.png](https://i-blog.csdnimg.cn/blog_migrate/cce5ed51f451e7fe054a641370f4a0b8.jpeg)
- 再让加钱参与者准备加钱,如果失败,则回滚本地和分布式事务
看下加钱的try方法实现:
![2b06b556eddcacb660121101d0aae27a.png](https://i-blog.csdnimg.cn/blog_migrate/e2f9787ec7d7003b758982816de30101.jpeg)
- 如果上面两步都成功,则会分别调用各自的commit方法,如果方法有异常将会重试
firstAction 提交扣钱
![f911163bae54175f99fa32d643d3eef5.png](https://i-blog.csdnimg.cn/blog_migrate/24eb6a9812a6fa8032421afa0acafc48.jpeg)
- secondActin 提交加钱
![2d102c72f84b89fb398b0c9b97b0105f.png](https://i-blog.csdnimg.cn/blog_migrate/c3e414cb56294871134478134c1e2778.jpeg)
- 如果firstAction和secondAction的try方法有异常将会自动调用各自的rollback方法:
![16861bf08280078b9dca774e75420aed.png](https://i-blog.csdnimg.cn/blog_migrate/2e6c9dd88785a9b5a4201b94b1463fda.jpeg)
![16a1d4a9a91e6ee15eebef7d66ac9e96.png](https://i-blog.csdnimg.cn/blog_migrate/47f688970da708614459323c564777f5.jpeg)
总结
整体来看TCC的模式编码还是比较简单的,不过还是有几点需要注意:
- 根据业务设计好tcc的三个方法
- 接口幂等
- 允许空回滚
- 防悬挂控制(参考 https://blog.csdn.net/hosaos/article/details/89136666 )
可以在二阶段执行时插入一条事务控制记录,状态为已回滚,这样当一阶段执行时,先读取该记录,如果记录存在,就认为二阶段回滚操作已经执行,不再执行try方法。