微服务seata 1.4.2 分布式事务TCC模式示例
seata TCC模式和AT模式的基础环境是一样的,只是在实现方式上有所区别,而且TCC模式还可以和AT模式混合使用。
关于AT模式示例,可以参考seata 1.4.2 分布式事务AT模式示例。
TCC模式的优势是灵活性,不依赖于数据库的事务特性来实现两阶段提交,而是采用代码来实现。对于无法完全依赖于数据库事务特性的分布式事务,就可以考虑使用TCC模式。、
当然TCC模式的灵活性,也就带来了复杂性,因为不能依赖于数据库事务的提交和回滚机制,就需要在代码中,针对每个需要实现分布式事务特性的业务操作,添加提交和回滚处理。
1. TCC模式代码实现
1.1 TCC接口定义
这里以一个业务为例说明,其它业务参考该模式实现即可。
package com.platform.account.tcc.service;import com.platform.account.domain.Account;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;@LocalTCC
public interface AccountTccService {/*** Prepare boolean.** @param actionContext the action context* @param account the account* @param price the price* @return the boolean*/@TwoPhaseBusinessAction(name = "AccountTccService", commitMethod = "commit", rollbackMethod = "rollback")public boolean prepare(BusinessActionContext actionContext,@BusinessActionContextParameter(paramName = "account") Account account,@BusinessActionContextParameter(paramName = "price") Double price);/*** Commit boolean.** @param actionContext the action context* @return the boolean*/public boolean commit(BusinessActionContext actionContext);/*** Rollback boolean.** @param actionContext the action context* @return the boolean*/public boolean rollback(BusinessActionContext actionContext);
}
接口需要增加一个注解@LocalTCC,接口定义了三个方法,其中prepare是第一阶段提交,commit和rollback是第二阶段提交。
1.2 TCC接口实现
package com.platform.account.tcc.service.impl;import com.platform.account.domain.Account;
import com.platform.account.service.AccountService;
import com.platform.account.tcc.service.AccountTccService;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;@Service
public class AccountTccServiceImpl implements AccountTccService {@Autowiredprivate AccountService accountService;@Overridepublic boolean prepare(BusinessActionContext actionContext, Account account, Double price) {String xid = actionContext.getXid();System.out.println("TccActionOne prepare, xid:" + xid + ", account:" + account);int res = accountService.reduceBalance(account.getId(), price);if(res > 0){return true;}else {return false;}}@Overridepublic boolean commit(BusinessActionContext actionContext) {String xid = actionContext.getXid();System.out.println("TccActionOne commit, xid:" + xid + ", account:" + actionContext.getActionContext("account"));return true;}@Overridepublic boolean rollback(BusinessActionContext actionContext) {String xid = actionContext.getXid();System.out.println("TccActionOne rollback, xid:" + xid + ", account:" + actionContext.getActionContext("account"));// ResultHolder.setActionOneResult(xid, "R");return true;}
}
1.3 TCC接口的调用
package com.platform.account.controller;import com.platform.account.domain.Account;
import com.platform.account.handler.AjaxResult;
import com.platform.account.service.AccountService;
import com.platform.account.tcc.service.AccountTccService;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;@RestController
public class AccountController {@Autowiredprivate AccountTccService accountTccService;/*** 扣减库存接口* @param userId* @param price*/@PostMapping("/reduceBalance")AjaxResult reduceBalance(@RequestParam("userId") Long userId, @RequestParam("price")Double price){BusinessActionContext actionContext = new BusinessActionContext();Account account = new Account();account.setId(userId);System.out.println("price = " + price); boolean res = accountTccService.prepare(actionContext, account, price);return AjaxResult.success(1);}}
2. TCC模式的异常处理
因为TCC模式需要在代码中处理各种异常,所以需要将各种情况考虑全面,因为在分布式环境中,出现网络超时、重发,机器宕机等一系列的异常,一旦这些异常情况没有处理,或者处理不合理,就可能导致业务数据错误。最常见的主要是这三种异常,分别是空回滚、幂等、悬挂。
2.1 空回滚
首先是空回滚。什么是空回滚?空回滚就是对于一个分布式事务,在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。
3.2 幂等
接下来是幂等。幂等就是对于同一个分布式事务的同一个分支事务,重复去调用该分支事务的第二阶段接口,因此,要求 TCC 的二阶段 Confirm 和 Cancel 接口保证幂等,不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致资损等严重问题。
3.3 悬挂
最后是防悬挂。按照惯例,咱们来先讲讲什么是悬挂。悬挂就是对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。因为允许空回滚的原因,Cancel 接口认为 Try 接口没执行,空回滚直接返回成功,对于 Seata 框架来说,认为分布式事务的二阶段接口已经执行成功,整个分布式事务就结束了。但是这之后 Try 方法才真正开始执行,预留业务资源,前面提到事务并发控制的业务加锁,对于一个 Try 方法预留的业务资源,只有该分布式事务才能使用,然而 Seata 框架认为该分布式事务已经结束,也就是说,当出现这种情况时,该分布式事务第一阶段预留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务资源预留后没法继续处理。
3. 异常控制实现
在分析完空回滚、幂等、悬挂等异常 Case 的成因以及解决方案以后,下面我们就综合起来考虑,一个 TCC 接口如何完整的解决这三个问题。
首先是 Try 方法。结合前面讲到空回滚和悬挂异常,Try 方法主要需要考虑两个问题,一个是 Try 方法需要能够告诉二阶段接口,已经预留业务资源成功。第二个是需要检查第二阶段是否已经执行完成,如果已完成,则不再执行。因此,Try 方法的逻辑可以如图所示:
先插入事务控制表记录,如果插入成功,说明第二阶段还没有执行,可以继续执行第一阶段。如果插入失败,则说明第二阶段已经执行或正在执行,则抛出异常,终止即可。
接下来是 Confirm 方法。因为 Confirm 方法不允许空回滚,也就是说,Confirm 方法一定要在 Try 方法之后执行。因此,Confirm 方法只需要关注重复提交的问题。可以先锁定事务记录,如果事务记录为空,则说明是一个空提交,不允许,终止执行。如果事务记录不为空,则继续检查状态是否为初始化,如果是,则说明一阶段正确执行,那二阶段正常执行即可。如果状态是已提交,则认为是重复提交,直接返回成功即可;如果状态是已回滚,也是一个异常,一个已回滚的事务,不能重新提交,需要能够拦截到这种异常情况,并报警。
最后是 Cancel 方法。因为 Cancel 方法允许空回滚,并且要在先执行的情况下,让 Try 方法感知到 Cancel 已经执行,所以和 Confirm 方法略有不同。首先依然是锁定事务记录。如果事务记录为空,则认为 Try 方法还没执行,即是空回滚。空回滚的情况下,应该先插入一条事务记录,确保后续的 Try 方法不会再执行。如果插入成功,则说明 Try 方法还没有执行,空回滚继续执行。如果插入失败,则认为 Try 方法正再执行,等待 TC 的重试即可。如果一开始读取事务记录不为空,则说明 Try 方法已经执行完毕,再检查状态是否为初始化,如果是,则还没有执行过其他二阶段方法,正常执行 Cancel 逻辑。如果状态为已回滚,则说明这是重复调用,允许幂等,直接返回成功即可。如果状态为已提交,则同样是一个异常,一个已提交的事务,不能再次回滚。
4. 总结
通过上述的说明,可以看出TCC模式还是比较复杂的,该模式需要对业务规则有清晰和明确的定义,只有在业务规则清楚的情况下,针对各种异常情况进行全面的处理,才能确保TCC模式下的分布式事务的正确性和完整性。
示例中只是演示了TCC模式的接口实现,并没有对各种异常信息进行处理,主要是异常处理与业务特点和规则是密切关联的。有了原则性的方向,再结合业务特点,使用TCC模式实现分布式业务处理,应该说,还是具有较高可行性的。
参考代码下载
微服务seata 1.4.2 分布式事务TCC模式示例相关推荐
- .Net Core with 微服务 - 可靠消息最终一致性分布式事务
前面我们讲了分布式事务的2PC.3PCTCC 的原理.这些事务其实都在尽力的模拟数据库的事务,我们可以简单的认为他们是一个同步行的事务.特别是 2PC,3PC 他们完全利用数据库的事务能力,在一阶段开 ...
- Seata多微服务互相调用_全局分布式事务使用案例_seata之原理剖析---微服务升级_SpringCloud Alibaba工作笔记0066
技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 通过上面的案例,我们已经可以会使用seata进行全局的分布式事物的使用了,在多微服务相互调用的环境 ...
- SpringCloud Alibaba 微服务架构(十一)- 分布式事务解决方案及理论基础篇
前言 在传统的单体应用架构中,例如经典的SSM,项目会采用分层架构模式:数据库访问层.业务逻辑层.控制层,从前端到后台所有的代码都是一个或者几个开发者去完成,该架构模式没有对我们业务逻辑代码实现拆分. ...
- dubbo调用超时回滚_微服务痛点基于Dubbo + Seata的分布式事务(AT模式)
前言 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案. ...
- 探秘蚂蚁金服分布式事务 Seata 的AT、Saga和TCC模式
作者| 屹远(陈龙),蚂蚁金服分布式事务核心研发 . 导语 本文根据 SOFA Meetup#3 广州站 <分布式事务 Seata 及其三种模式详解>主题分享整理,着重分享分布式事务产生的 ...
- 秒杀项目之网关服务限流熔断降级分布式事务
目录 一.网关服务限流熔断降级 二.Seata--分布式事务 2.1 分布式事务基础 2.1.1 事务 2.1.2 本地事务 2.1.3 分布式事务 2.1.4 分布式事务场景 2.2 分布式事务解决 ...
- 多个mapper的事务回滚_揭秘蚂蚁金服分布式事务 Seata 的AT、Saga和TCC模式
作者| 屹远(陈龙),蚂蚁金服分布式事务核心研发 . 导语 本文根据 8月11日 SOFA Meetup#3 广州站 <分布式事务 Seata 及其三种模式详解>主题分享整理,着重分享分布 ...
- python 微服务架构实战_《分布式服务架构:原理、设计与实战》第一章分布式微服务架构设计原理...
1.从传统单体架构到服务化架构 1.1 JEE架构 JEE将企业级软件架构分为三个层级 : Web 层.业务逻辑层和数据存取层.对应的职能团队,主要包括:用户 交互 UI 团队.后台业务逻辑处理团 队 ...
- ShardingSphere RAW JDBC 分布式事务XA 代码示例
ShardingSphere RAW JDBC 分布式事务XA 代码示例 项目工程在:transaction-2pc-xa-raw-jdbc-example 代码简介 基于ShardingSp ...
最新文章
- 百度推出LinearDesign,全球首个mRNA疫苗不稳定性解决方案,仅需16分钟
- 容器生态系统 (续) - 每天5分钟玩转容器技术(3)
- [leetcode]102.二叉树的层序遍历
- 深入了解计算机网络参考模型
- c/s三层结构信息系统的三个层次_网络资讯:三层架构是什么
- 解放计算给服务带来的压力,第一想到的就是阿里云高性能计算(HPC)
- Ubuntu18.04配置TeXLive2020+TeXstudio
- 大道至简 7、8、读后感
- vue中父子组件先后渲染_Vue中父子组件执行的先后顺序
- 腾讯校园招聘笔试 2019-8-17 第五题
- linux常用知识命令
- Eigen3卸载与安装
- 为何你的网络爬虫技术提升缓慢?甚至小白无从下手学习?
- Python引力波火了 你该了解的开源框架
- 夏昕.深入浅出Hibernate中的第一个例子体会.
- 微信小程序实时音视频功能简析(live-pusher与live-player)
- 防止跨站攻击——CSRFToken
- jconsole是否可以在生产环境使用_使用JCONSOLE远程监控JVM
- matlab整流仿真,基于MATLAB的整流电路的建模与仿真
- RF- BuiltIn_Run Keyword关键字系列