天增的博客
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
  • 分布式解决方案
  • 分布式理论
    • 三驾马车
      • The Google File System
      • BigTable中文翻译
      • MapReduce
    • 理论基础
    • 共识算法
      • Raft算法
      • Paxos算法
    • 通讯协议-Gossip
  • 流量调度
    • 流量控制
    • 服务路由
    • 负载均衡
  • 数据调度
    • 分布式缓存
    • 数据库
    • 分布式事务
      • 事务分类
        • 刚性事务的实现
        • 柔性事务的实现
        • 总体的⽅案对⽐
      • Seata框架
        • Seata解决方案
          • AT模式
          • TCC模式
            • Sega模式
            • XA模式
      • 分布式锁
      • 分布式ID
    • 服务治理
      • 服务注册和发现
      • 链路追踪
      • 服务监控
      • 服务降级和熔断
    • 高并发架构
    • topic
    • 分布式解决方案
    • 数据调度
    • 分布式事务
    • Seata框架
    • Seata解决方案
    • TCC模式
    2022-04-26
    目录

    TCC模式

    # TCC模式

    TCC 与 Seata AT 事务⼀样都是两阶段事务,它与 AT 事务的主要区别为:

    • TCC 对业务代码侵⼊严重

      每个阶段的数据操作都要⾃⼰进⾏编码来实现,事务框架⽆法⾃动处理。

    • TCC 性能更⾼

      不必对数据加全局锁,允许多个事务同时操作数据。

    ecaeda572495e4ea5e308fb938a38fd5

    Seata TCC整体是两阶段提交的模型。

    ⼀个分布式的全局事务,全局事务是由若⼲分⽀事务组成的,分⽀事务要满⾜两阶段提交的模型要求,即需要每个分⽀事务都具备⾃⼰的:

    • ⼀阶段 prepare ⾏为
    • ⼆阶段 commit 或 rollback ⾏为

    TCC 模式,不依赖于底层数据资源的事务⽀持:

    • ⼀阶段 prepare ⾏为:调⽤ ⾃定义 的 prepare 逻辑。

    • ⼆阶段 commit ⾏为:调⽤ ⾃定义 的 commit 逻辑。

    • ⼆阶段 rollback ⾏为:调⽤ ⾃定义 的 rollback 逻辑。

    TCC 模式,是指⽀持把⾃定义的分⽀事务纳⼊到全局事务的管理中。

    ‍

    # 第⼀阶段 Try

    以账户服务为例,当下订单时要扣减⽤户账户⾦额:

    假如⽤户购买 100 元商品,要扣减 100 元。

    TCC 事务⾸先对这100元的扣减⾦额进⾏预留,或者说是先冻结这100元

    # 第⼆阶段 Confirm

    如果第⼀阶段能够顺利完成,那么说明“扣减⾦额”业务(分⽀事务)最终肯定 是可以成功的。

    当全局事务提交时, TC会控制当前分⽀事务进⾏提交,如 果提交失败,TC 会反复尝试,直到提交成功为⽌。

    当全局事务提交时,就可以使⽤冻结的⾦额来最终实现业务数据操作:

    # 第⼆阶段 Cancel

    如果全局事务回滚,就把冻结的⾦额进⾏解冻,恢复到以前的状态,TC 会 控制当前分⽀事务回滚,如果回滚失败,TC 会反复尝试,直到回滚完成为⽌。

    image.png

    # 多个事务并发的情况

    多个TCC全局事务允许并发,它们执⾏扣减⾦额时,只需要冻结各⾃的⾦额即可:

    # SpringCloud集成TCC

    # 定义TCC接口

    由于我们使用的是 SpringCloud + Feign,Feign的调用基于http,因此此处我们使用@LocalTCC便可。值得注意的是,@LocalTCC一定需要注解在接口上,此接口可以是寻常的业务接口,只要实现了TCC的两阶段提交对应方法便可,TCC相关注解如下:

    • @LocalTCC 适用于SpringCloud+Feign模式下的TCC
    • @TwoPhaseBusinessAction 注解try方法,其中name为当前tcc方法的bean名称,写方法名便可(全局唯一),commitMethod指向提交方法,rollbackMethod指向事务回滚方法。指定好三个方法之后,seata会根据全局事务的成功或失败,去帮我们自动调用提交方法或者回滚方法。
    • @BusinessActionContextParameter 注解可以将参数传递到二阶段(commitMethod/rollbackMethod)的方法。
    • BusinessActionContext 便是指TCC事务上下文
    /**
     * 这里定义tcc的接口
     * 一定要定义在接口上
     * 我们使用springCloud的远程调用
     * 那么这里使用LocalTCC便可
     *
     * @author tanzj
     */
    @LocalTCC
    public interface TccService {
     
        /**
         * 定义两阶段提交
         * name = 该tcc的bean名称,全局唯一
         * commitMethod = commit 为二阶段确认方法
         * rollbackMethod = rollback 为二阶段取消方法
         * BusinessActionContextParameter注解 传递参数到二阶段中
         *
         * @param params  -入参
         * @return String
         */
        @TwoPhaseBusinessAction(name = "insert", commitMethod = "commitTcc", rollbackMethod = "cancel")
        String insert(
                @BusinessActionContextParameter(paramName = "params") Map<String, String> params
        );
     
        /**
         * 确认方法、可以另命名,但要保证与commitMethod一致
         * context可以传递try方法的参数
         *
         * @param context 上下文
         * @return boolean
         */
        boolean commitTcc(BusinessActionContext context);
     
        /**
         * 二阶段取消方法
         *
         * @param context 上下文
         * @return boolean
         */
        boolean cancel(BusinessActionContext context);
    }
    

    # TCC接口的业务实现

    为了保证代码的简洁,此处将路由层与业务层结合讲解,实际项目则不然。

    • 在try方法中使用@Transational可以直接通过spring事务回滚关系型数据库中的操作,而非关系型数据库等中间件的回滚操作可以交给rollbackMethod方法处理。
    • 使用context.getActionContext("params")便可以得到一阶段try中定义的参数,在二阶段对此参数进行业务回滚操作。
    • **注意1:**此处亦不可以捕获异常(同理切面处理异常),否则TCC将识别该操作为成功,二阶段直接执行commitMethod。
    • 注意2:TCC模式要开发者自行保证幂等和事务防悬挂
    @Slf4j
    @RestController
    public class TccServiceImpl implements  TccService {
     
        @Autowired
        TccDAO tccDAO;
     
        /**
         * tcc服务t(try)方法
         * 根据实际业务场景选择实际业务执行逻辑或者资源预留逻辑
         *
         * @param params - name
         * @return String
         */
        @Override
        @PostMapping("/tcc-insert")
        @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
        public String insert(@RequestBody Map<String, String> params) {
            log.info("xid = " + RootContext.getXID());
            //todo 实际的操作,或操作MQ、redis等
            tccDAO.insert(params);
            //放开以下注解抛出异常
            //throw new RuntimeException("服务tcc测试回滚");
            return "success";
        }
     
        /**
         * tcc服务 confirm方法
         * 若一阶段采用资源预留,在二阶段确认时要提交预留的资源
         *
         * @param context 上下文
         * @return boolean
         */
        @Override
        public boolean commitTcc(BusinessActionContext context) {
            log.info("xid = " + context.getXid() + "提交成功");
            //todo 若一阶段资源预留,这里则要提交资源
            return true;
        }
     
        /**
         * tcc 服务 cancel方法
         *
         * @param context 上下文
         * @return boolean
         */
        @Override
        public boolean cancel(BusinessActionContext context) {
            //todo 这里写中间件、非关系型数据库的回滚操作
            System.out.println("please manually rollback this data:" + context.getActionContext("params"));
            return true;
        }
    }
    
    最近更新
    01
    以 root 身份启动 transmission-daemon
    12-13
    02
    Debian系统安装qbittorrent-nox
    12-09
    03
    LXC Debain12安装zerotier并实现局域网自动nat转发
    07-29
    更多文章>
    Theme by Vdoing | Copyright © 2015-2024 天增 | 苏ICP备16037388号-1
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式