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

摘要: 原创出处 http://www.iocoder.cn/TCC-Transaction/console/ 「芋道源码」欢迎转载,保留摘要,谢谢!java

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


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:git

  1. RocketMQ / MyCAT / Sharding-JDBC 全部源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将获得认真回复。甚至不知道如何读源码也能够请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1. 概述

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

  • 查看未完成的事务列表
  • 重置事务恢复重试次数

运维平台( Maven 项目 tcc-transaction-server ) 总体代码结构以下:redis

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

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

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 数组 */
    List<TransactionVo> 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 提供了四种事务存储器,可是目前只支持两种数据访问层的实现:app

  • 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,配置数据源属性。
  • domainSuffixdomian 和 表后缀( 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 数组
   List<TransactionVo> 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 CommonResponse<Void> reset(@PathVariable String domain, String globalTxId, String branchQualifier) {
   transactionDao.resetRetryCount(domain,
           DatatypeConverter.parseHexBinary(globalTxId),
           DatatypeConverter.parseHexBinary(branchQualifier));
   return new CommonResponse<Void>();
}
复制代码

UI 界面以下:

666. 彩蛋

知识星球

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

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

固然,由于是开源的关系,咱们能够本身作运维平台反向的贡献到这些项目。

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

相关文章
相关标签/搜索