分布式事务 TCC-Transaction 源码分析 —— 运维平台

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 分布式事务 TCC-Transaction 源码分析 —— 运维平台

本文主要基于 TCC-Transaction 1.2.3.3 正式版

  • 1. 概述
  • 2. 数据访问层
    • 2.1 JDBC 事务 DAO
    • 2.2 Redis 事务 DAO
      1. 控制层
    • 3.1 查看未完成的事务列表
    • 3.2 重置事务恢复重试次数
      1. 彩蛋

      友情提示:欢迎关注公众号【芋道源码】。😈关注后,拉你进【源码圈】微信群讨论技术和源码。

      友情提示:欢迎关注公众号【芋道源码】。😈关注后,拉你进【源码圈】微信群讨论技术和源码。
      友情提示:欢迎关注公众号【芋道源码】。😈关注后,拉你进【源码圈】微信群讨论技术和源码。

      1. 概述

      本文分享 运维平台。TCC-Transaction 提供了相对精简的运维平台,用于查看在《TCC-Transaction 源码分析 —— 事务存储器》提到的事务存储。目前暂时只有两个功能:

    • 查看未完成的事务列表
    • 重置事务恢复重试次数
    • 运维平台( Maven 项目  tcc-transaction-server ) 整体代码结构如下:

      本文自下而上,Dao = Controller = UI 的顺序进行解析实现。

      你行好事会因为得到赞赏而愉悦  同理,开源项目贡献者会因为 Star 而更加有动力  为 TCC-Transaction 点赞!传送门

      ps:笔者假设你已经阅读过《tcc-transaction 官方文档 —— 使用指南1.2.x》。

      2. 数据访问层

      org.mengyun.tcctransaction.server.dao.TransactionDao,事务Dao 接口,实现代码如下:

      
      public interface TransactionDao {
      
          /**
           * 获得事务 VO 数组
           *
           * @param domain 领域
           * @param pageNum 第几页
           * @param pageSize 分页大小
           * @return 事务 VO 数组
           */
          ListTransactionVo findTransactions(String domain, Integer pageNum, int pageSize);
      
          /**
           * 获得事务总数量
           *
           * @param domain 领域
           * @return 数量
           */
          Integer countOfFindTransactions(String domain);
      
          /**
           * 重置事务重试次数
           *
           * @param domain 领域
           * @param globalTxId 全局事务编号
           * @param branchQualifier 分支事务编号
           * @return 是否重置成功
           */
          boolean resetRetryCount(String domain, byte[] globalTxId, byte[] branchQualifier);
      }
      

      TCC-Transaction 提供了四种事务存储器,但是目前只支持两种数据访问层的实现:

    • JDBC 事务 DAO
    • Redis 事务 DAO
    • 2.1 JDBC 事务 DAO

      org.mengyun.tcctransaction.server.dao.JdbcTransactionDao,JDBC 事务 DAO 实现。实现代码如下:

      
      @Repository("jdbcTransactionDao")
      public class JdbcTransactionDao implements TransactionDao {
      
          private static final String TABLE_NAME_PREFIX = "TCC_TRANSACTION";
      
          @Autowired
          private DataSource dataSource;
      
          /**
           * 读取 jdbc-domain-suffix.properties
           */
          @Value("#{jdbcDomainSuffix}")
          private Properties domainSuffix;
      
          // ... 省略代码
      }
      
    • `dataSource`,数据源。配置方式如下:
      // appcontext-server-dao.xml 
      bean id="dataSource"
           class="org.apache.commons.dbcp.BasicDataSource"
           destroy-method="close"
         property name="driverClassName" value="com.mysql.jdbc.Driver"/
         property name="url" value="${jdbc.url}"/
         property name="username" value="${jdbc.username}"/
         property name="password" value="${jdbc.password}"/
         property name="maxActive" value="50"/
         property name="minIdle" value="5"/
         property name="maxIdle" value="20"/
         property name="initialSize" value="30"/
         property name="logAbandoned" value="true"/
         property name="removeAbandoned" value="true"/
         property name="removeAbandonedTimeout" value="10"/
         property name="maxWait" value="1000"/
         property name="timeBetweenEvictionRunsMillis" value="10000"/
         property name="numTestsPerEvictionRun" value="10"/
         property name="minEvictableIdleTimeMillis" value="10000"/
         property name="validationQuery" value="SELECT NOW() FROM DUAL"/
      /bean
      
      

      // tcc-transaction-server.properties
      jdbc.url=jdbc:mysql://127.0.0.1:33061/TCC?useUnicode=true&characterEncoding=UTF-8
      jdbc.username=root
      jdbc.password=123456

      • 在 `appcontext-server-dao.xml`,配置数据源 Bean 对象。
      • 在 `tcc-transaction-server.properties`,配置数据源属性。
      • domainSuffix domian 和 表后缀(  suffix ) 的映射关系。配置方式如下:

        
        // jdbc-domain-suffix.properties
        CAPITAL=_CAP
        ORDER=_ORD
        REDPACKET=_RED
        
      • 键 :domain。
      • 值 :suffix。
      • JdbcTransactionDao 代码实现上比较易懂,点击链接查看,已经添加中文注释。

        2.2 Redis 事务 DAO

        org.mengyun.tcctransaction.server.dao.RedisTransactionDao,Redis 事务 DAO。实现代码如下:

        
        @Repository("redisTransactionDao")
        public class RedisTransactionDao implements TransactionDao {
        
            /**
             * redis pool
             */
            @Autowired
            private JedisPool jedisPool;
        
            /**
             * 序列化
             */
            private ObjectSerializer serializer = new JdkSerializationSerializer();
        
            /**
             * 读取 redis-domain-key-prefix.properties
             */
            @Value("#{redisDomainKeyPrefix}")
            private Properties domainKeyPrefix;
        }
        
      • `jedisPool`,Redis 连接池。配置方式如下:
        // appcontext-server-dao.xml
        bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"
           property name="maxTotal" value="300"/
           property name="maxIdle" value="100"/
           property name="minIdle" value="10"/
           property name="maxWaitMillis" value="3000"/
        /bean
        
        

        bean id=”jedisPool” class=”redis.clients.jedis.JedisPool”
          constructor-arg index=”0” ref=”jedisPoolConfig”/
          constructor-arg index=”1” value=”${redis.host}”/
          constructor-arg index=”2” value=”${redis.port}” type=”int”/
          constructor-arg index=”3” value=”6000” type=”int”/
          constructor-arg index=”4” type=”java.lang.String”
              null/
          /constructor-arg
          constructor-arg index=”5” value=”${redis.db}” type=”int”/
        /bean

        // tcc-transaction-server.properties
        redis.host=127.0.0.1
        redis.port=6379
        redis.password=
        redis.db=0

        • 在 `appcontext-server-dao.xml`,配置 Redis 连接池 Bean 对象。
        • 在 `tcc-transaction-server.properties`,配置 Redis 连接池属性。
        • domainKeyPrefix,domain 和 Redis Key 前缀(  prefix )的映射。配置方式如下:

          
          CAPITAL=TCC:CAP:
          ORDER=TCC:ORD:
          REDPACKET=TCC:RED:
          
        • 键 :domain。
        • 值 :suffix。
        • RedisTransactionDao 代码实现上比较易懂,点击[链接]https://github.com/YunaiV/tcc-transaction/blob/e54c3e43a2e47a7765bdb18a485860cb31acbb72/tcc-transaction-server/src/main/java/org/mengyun/tcctransaction/server/dao/RedisTransactionDao.java)查看,已经添加中文注释。

          3. 控制层

          org.mengyun.tcctransaction.server.controller.TransactionController,事务 Controller。实现代码如下:

          
          @Controller
          public class TransactionController {
          
              public static final Integer DEFAULT_PAGE_NUM = 1;
          
              public static final int DEFAULT_PAGE_SIZE = 10;
          
              /**
               * 数据访问对象
               */
              @Autowired
              @Qualifier("jdbcTransactionDao")
              private TransactionDao transactionDao;
          
              /**
               * 项目访问根目录
               */
              @Value("${tcc_domain}")
              private String tccDomain;
          }
          
        • `transactionDao`,数据访问对象。配置方式如下:
          // appcontext-server-dao.xml
          bean id="transactionDao" class="org.mengyun.tcctransaction.server.dao.JdbcTransactionDao"/
          
          • 目前运维平台只能读取一个数据源,如果你的数据源是多个,需要对运维平台做一定的改造,或启动多个项目。
          • tccDomain,项目访问根目录。配置方式如下:

            
            // tcc-transaction-server.properties
            tcc_domain=
            
          • 一般情况下不用配置,如果你放在 Tomcat 根目录。
          • 3.1 查看未完成的事务列表

            调用  TransactionController#manager(...) 方法,查看事务列表。实现代码如下:

            
            @RequestMapping(value = "/management", method = RequestMethod.GET)
            public ModelAndView manager() {
               return new ModelAndView("manager");
            }
            
            @RequestMapping(value = "/management/domain/{domain}", method = RequestMethod.GET)
            public ModelAndView manager(@PathVariable String domain) {
               return manager(domain, DEFAULT_PAGE_NUM);
            }
            
            @RequestMapping(value = "/management/domain/{domain}/pagenum/{pageNum}", method = RequestMethod.GET)
            public ModelAndView manager(@PathVariable String domain, @PathVariable Integer pageNum) {
               ModelAndView modelAndView = new ModelAndView("manager");
               // 获得事务 VO 数组
               ListTransactionVo transactionVos = transactionDao.findTransactions(domain, pageNum, DEFAULT_PAGE_SIZE);
               // 获得事务总数量
               Integer totalCount = transactionDao.countOfFindTransactions(domain);
               // 计算总页数
               Integer pages = totalCount / DEFAULT_PAGE_SIZE;
               if (totalCount % DEFAULT_PAGE_SIZE  0) {
                   pages++;
               }
               // 返回
               modelAndView.addObject("transactionVos", transactionVos);
               modelAndView.addObject("pageNum", pageNum);
               modelAndView.addObject("pageSize", DEFAULT_PAGE_SIZE);
               modelAndView.addObject("pages", pages);
               modelAndView.addObject("domain", domain);
               modelAndView.addObject("urlWithoutPaging", tccDomain + "/management/domain/" + domain);
               return modelAndView;
            }
            

            UI 界面如下:

            3.2 重置事务恢复重试次数

            调用  TransactionController#reset(...) 方法,事务重置重试次数。实现代码如下:

            
            @RequestMapping(value = "/domain/{domain}/retry/reset", method = RequestMethod.PUT)
            @ResponseBody
            public CommonResponseVoid reset(@PathVariable String domain, String globalTxId, String branchQualifier) {
               transactionDao.resetRetryCount(domain,
                       DatatypeConverter.parseHexBinary(globalTxId),
                       DatatypeConverter.parseHexBinary(branchQualifier));
               return new CommonResponseVoid();
            }
            

            UI 界面如下:

            666. 彩蛋

            可能有人会吐槽运维平台怎么做的这么简陋。这个不是 TCC-Transaction 一个开源项目存在的问题,其他例如 Dubbo、Disconf 等等都会存在这个情况。

            开源作者因为时间关系,更多的精力关注在核心代码,所以对运维友好性可能花费的精力较少。

            当然,因为是开源的关系,我们可以自己做运维平台反向的贡献到这些项目。

            胖友,分享一个朋友圈可好?

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 分布式事务 TCC-Transaction 源码分析 —— 运维平台


     上一篇
    从一次 Snowflake 异常说起 从一次 Snowflake 异常说起
    本文主要基于真实踩坑经历展开 1. 异常概述 2. 原因分析 2.1 Snowflake工作原理 2.2 问题定位 2.3 排除时钟回拨 2.4 研究workerid 2.5 疑点 原因分析 2.2 问题
    2021-04-05
    下一篇 
    看透 Spring MVC 源代码分析与实践 —— 网站基础知识 看透 Spring MVC 源代码分析与实践 —— 网站基础知识
    网站架构及其演变过程 基础结构网络传输分解方式: 标准的 OSI 参考模型 TCP/IP 参考模型 TCP/IP 参考模型 海量数据的解决方案 缓存和页面静态化 缓存 通过程序直接保存在内存中 使用缓
    2021-04-05