本指南适用于tcc-transaction 1.2.x版本。tcc-transaction 1.1.x版的使用指南请参考:使用指南1.1.x

1.2.x支持rpc框架支持隐式传参情况下将事务上下文参数TransactionContext以隐式传参方式进行传递,目前支持rpc框架为dubbo时的隐式传参方式传递,具体配置可参考下面“rpc框架为dubbo时隐式传参方式配置”。
_
国内主要的开源TCC分布式事务框架包括:
框架名称 Github地址 star数量
tcc-transaction https://github.com/changmingxie/tcc-transaction 2446
Hmily https://github.com/yu199195/hmily 1381
ByteTCC https://github.com/liuyangming/ByteTCC 1300
EasyTransaction https://github.com/QNJR-GROUP/EasyTransaction 904

分布式事务研究以及功能测试相关结果(RPC、事务日志没有全部验证)
image.png

1. 配置tcc-transaction

1 引用tcc-transaction

在服务调用方和提供方项目中需要引用tcc-transaction-spring jar包,如使用maven依赖:

  1. <dependency>
  2. <groupId>org.mengyun</groupId>
  3. <artifactId>tcc-transaction-spring</artifactId>
  4. <version>${project.version}</version>
  5. </dependency>

2 加载tcc-transaction.xml配置

启动应用时,需要将tcc-transaction-spring jar中的tcc-transaction.xml加入到classpath中。如在web.xml中配置:

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath:tcc-transaction.xml
  4. </param-value>
  5. </context-param>

3 设置TransactionRepository

需要为参与事务的应用项目配置一个TransactionRepository,tcc-transaction框架使用transactionRepository持久化事务日志。可以选择FileSystemTransactionRepository、SpringJdbcTransactionRepository、RedisTransactionRepository或ZooKeeperTransactionRepository。

使用SpringJdbcTransactionRepository配置示例如下:

  1. <bean id="transactionRepository"
  2. class="org.mengyun.tcctransaction.spring.repository.SpringJdbcTransactionRepository">
  3. <property name="dataSource" ref="dataSource"/>
  4. </bean>
  5. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
  6. destroy-method="close">
  7. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  8. <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
  9. <property name="username" value="root"/>
  10. <property name="password" value=""/>
  11. </bean>

特别提示,dataSource需要单独配置,不能和业务里使用的dataSource复用,即使使用的是同一个数据库。

使用RedisTransactionRepository配置示例如下(需配置redis服务器为AOF模式并在redis.conf中设置appendfsync为always以防止日志丢失):

  1. <bean id="transactionRepository" class="org.mengyun.tcctransaction.repository.RedisTransactionRepository">
  2. <property name="keyPrefix" value="tcc_ut_"/>
  3. <property name="jedisPool" ref="jedisPool"/>
  4. </bean>
  5. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  6. <property name="maxTotal" value="1000"/>
  7. <property name="maxWaitMillis" value="1000"/>
  8. </bean>
  9. <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
  10. <constructor-arg index="0" ref="jedisPoolConfig"/>
  11. <constructor-arg index="1" value="127.0.0.1"/>
  12. <constructor-arg index="2" value="6379" type="int"/>
  13. <constructor-arg index="3" value="1000" type="int"/>
  14. <!--<constructor-arg index="4" value="${redis.password}"/>-->
  15. </bean>

使用ZooKeeperTransactionRepository配置示例如下:

  1. <bean id="transactionRepository"
  2. class="org.mengyun.tcctransaction.repository.ZooKeeperTransactionRepository">
  3. <!--<property name="zkServers" value="localhost:2181,localhost:2183,localhost:2185"/>-->
  4. <property name="zkServers" value="localhost:2181"/>
  5. <property name="zkTimeout" value="10000"/>
  6. <property name="zkRootPath" value="/tcc_ut"/>
  7. </bean>

使用FileSystemTransactionRepository配置如下(FileSystemTransactionRepository仅适用事务发布方或调用方应用节点为单节点场景,因为日志是存储在应用节点本地文件中):

  1. <bean id="transactionRepository" class="org.mengyun.tcctransaction.repository.FileSystemTransactionRepository">
  2. <property name="rootPath" value="/data/tcc"/>
  3. </bean>

4 设置恢复策略(可选)

当Tcc事务异常后,恢复Job将会定期恢复事务。在Spring配置文件中配置RecoverConfig类型的Bean来设置恢复策略示例:

  1. <bean class="org.mengyun.tcctransaction.spring.recover.DefaultRecoverConfig">
  2. <property name="maxRetryCount" value="30"/>
  3. <property name="recoverDuration" value="120"/>
  4. <property name="cronExpression" value="0 */1 * * * ?"/>
  5. <property name="delayCancelExceptions">
  6. <util:set>
  7. <value>com.alibaba.dubbo.remoting.TimeoutException</value>
  8. </util:set>
  9. </property>
  10. </bean>

其中maxRetryCount表示一个事务最多尝试恢复次数,超过将不再自动恢复,需要人工干预,默认是30次。

recoverDuration表示一个事务日志当超过一定时间间隔后没有更新就会被认为是发生了异常,需要恢复,恢复Job将扫描超过这个时间间隔依旧没有更新的事务日志,并对这些事务进行恢复,时间单位是秒,默认是120秒。

cronExpression表示恢复Job触发间隔配置,默认是0 /1 ?。

delayCancelExceptions(1.2.3版中新加的配置)表示系统发生了设置的异常时,主事务不立即rollback,而是由恢复job来执行事务恢复。通常需要将超时异常设置为delayCancelExceptions,这样可以避免因为服务调用时发生了超时异常,主事务如果立刻rollback, 但是从事务还没执行完,从而造成主事务rollback失败。示例中com.alibaba.dubbo.remoting.TimeoutException为底层rpc框架为dubbo,超时异常发生时框架抛出的超时异常类,需要将其加入delayCancelExceptions中。

2. 发布Tcc服务

发布一个Tcc服务方法,可被远程调用并参与到Tcc事务中,发布Tcc服务方法有下面四个约束:

  1. 在服务提供方的实现方法上加上@Compensable注解,并设置注解的属性
  2. 服务方法第一个入参类型为org.mengyun.tcctransaction.api.TransactionContext
  3. 服务方法的入参能被序列化(默认使用jdk序列化机制,需要参数实现Serializable接口,可以设置repository的serializer属性自定义序列化实现)
  4. try方法、confirm方法和cancel方法入参类型须一样

Compensable的属性包括propagation、confirmMethod、cancelMethod、transactionContextEditor。propagation可不用设置,框架使用缺省值;设置confirmMethod指定CONFIRM阶段的调用方法;设置cancelMethod指定CANCEL阶段的调用方法;设置transactionContextEditor为MethodTransactionContextEditor.class。与1.1.x版本不同,从1.2.x起,TransactionContext将提供可配置的传递方式,不再要求服务方法第一个org.mengyun.tcctransaction.api.TransactionContext类型的入参为预留入参,而是可以自定义逻辑将TransactionContext以显示或隐式传参方式在调用方和服务提供方间传递。tcc-transaction框架使用transactionContextEditor来实现获取和调用方向服务提供方传递TransactionContext。如果底层服务框架使用的是dubbo,可以设置transactionContextEditor为DubboTransactionContextEditor.class(在tcc-transaction-dubbo.jar中,使用dubbo隐式传参方式),如果TransactionContext通过服务提供方方法参数形式传递(正如1.1.x版实现),则可设置transactionContextEditor为MethodTransactionContextEditor.class(在tcc-transaction-core.jar中)。

tcc-transaction将拦截加上了@Compensable注解的服务方法,并根据Compensalbe的confirmMethod和cancelMethod获取在CONFRIM阶段和CANCEL阶段需要调用的方法。注解属性tcc-transaction在调用confirmMethod或是cancelMethod时是根据发布Tcc服务的接口类在Spring的ApplicationContext中获取Tcc服务实例,并调用confirmMethod或cancelMethod指定方法。因此如果是使用动态代理的方式实现aop(默认方式),则confirmMethod和cancelMethod需在接口类中声明,如果使用动态字节码技术实现aop(如指定aspectj-autoproxy的proxy-target-class属性为true,在1.2.1版本中,默认已设置为true),则无需在接口类中声明。

tcc-transaction在执行服务过程中会将Tcc服务的上下文持久化,包括所有入参,内部默认实现为将入参使用jdk自带的序列化机制序列化为为byte流,所以需要实现Serializable接口(另外可选择Kryo序列化实现机制,可参考例子)。

2.1 在tcc-transaction-http-capital中发布Tcc服务示例:

try接口方法:

  1. public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);

try实现方法:

  1. @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
  2. public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

confirm方法:

  1. public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

cancel方法:

  1. public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

2.1.1 在tcc-transaction-http-redpacket中发布Tcc服务示例:

try接口方法:

  1. public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto);

try实现方法:

  1. @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
  2. public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

confirm方法:

  1. public void confirmRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

cancel方法:

  1. public void cancelRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

3. 调用远程Tcc服务

调用远程Tcc服务,将远程Tcc服务参与到本地Tcc事务中,本地的服务方法也需要声明为Tcc服务,与发布一个Tcc服务不同,本地Tcc服务方法有三个约束:

  1. 在服务方法上加上@Compensable注解,并设置注解属性
  2. 服务方法的入参都须能序列化(实现Serializable接口)
  3. try方法、confirm方法和cancel方法入参类型须一样

与发布Tcc服务不同的是本地Tcc服务Compensable注解属性transactionContextEditor可以不用设置。

本地服务通过远程tcc服务提供的client来调用,需要将这些tcc服务的client声明为可加入到TCC事务中,如tcc服务的client方法明为tryXXX,则需要在方法tryXXX上加上如下配置:

  1. @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "tryXXX", cancelMethod = "tryXXX", transactionContextEditor = MethodTransactionContextEditor.class)

其中propagation = Propagation.SUPPORTS表示该方法支持参与到TCC事务中。 如果tcc服务的client为框架自动生成实现(比如代理机制实现)不能添加注解,可为该client实现一个代理类,在代理类的方法上加上注解。

3.1 在tcc-transaction-http-order中调用远程Tcc服务示例:

try方法:

  1. @Compensable(confirmMethod = "confirmMakePayment",cancelMethod = "cancelMakePayment")
  2. public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
  3. System.out.println("order try make payment called");
  4. order.pay(redPacketPayAmount, capitalPayAmount);
  5. orderRepository.updateOrder(order);
  6. capitalTradeOrderService.record(null, buildCapitalTradeOrderDto(order));
  7. redPacketTradeOrderService.record(null, buildRedPacketTradeOrderDto(order));
  8. }

confirm方法:

  1. public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

cancel方法:

  1. public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

tcc服务提供的client方法增加注解:

  1. @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
  2. public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

4. rpc框架为dubbo时以隐式传参方式配置

rpc框架为dubbo时支持以隐式传参方式配置TCC事务。

4.1 配置tcc-transaction

与上面配置tcc-transaction一样,此外,还需要如下步骤:

  1. 引用tcc-transaction-dubbo 在项目中需要引用tcc-transaction-dubbo jar包,如使用maven依赖:

    1. <dependency>
    2. <groupId>org.mengyun</groupId>
    3. <artifactId>tcc-transaction-dubbo</artifactId>
    4. <version>${project.version}</version>
    5. </dependency>
  2. 加载tcc-transaction-dubbo.xml配置

需要将tcc-transaction-dubbo jar中的tcc-transaction-dubbo.xml加入到classpath中。如在web.xml中配置改为:

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath:tcc-transaction.xml,classpath:tcc-transaction-dubbo.xml
  4. </param-value>
  5. </context-param>

4.2 发布Tcc服务

发布一个Tcc服务方法,可被远程调用并参与到Tcc事务中,发布支持隐式传参的Tcc服务方法有下面四个约束:

  1. 在服务提供方的实现方法上加上@Compensable注解,并设置注解的属性
  2. 在服务提供方的接口方法上加上@Compensable注解
  3. 服务方法的入参能被序列化(默认使用jdk序列化机制,需要参数实现Serializable接口,可以设置repository的serializer属性自定义序列化实现)
  4. try方法、confirm方法和cancel方法入参类型须一样

Compensable的属性包括propagation、confirmMethod、cancelMethod、transactionContextEditor。propagation可不用设置,框架使用缺省值;设置confirmMethod指定CONFIRM阶段的调用方法;设置cancelMethod指定CANCEL阶段的调用方法;设置transactionContextEditor为DubboTransactionContextEditor.class。

4.2.1 在tcc-transaction-dubbo-capital中发布Tcc服务示例:

try接口方法:

  1. @Compensable
  2. public String record(CapitalTradeOrderDto tradeOrderDto);

try实现方法:

  1. @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
  2. public String record(CapitalTradeOrderDto tradeOrderDto) {

confirm方法:

  1. public void confirmRecord(CapitalTradeOrderDto tradeOrderDto) {

cancel方法:

  1. public void cancelRecord(CapitalTradeOrderDto tradeOrderDto) {

4.2.2 在tcc-transaction-dubbo-redpacket中发布Tcc服务示例:

try接口方法:

  1. @Compensable
  2. public String record(RedPacketTradeOrderDto tradeOrderDto);

try实现方法:

  1. @Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
  2. public String record(RedPacketTradeOrderDto tradeOrderDto) {

confirm方法:

  1. public void confirmRecord(RedPacketTradeOrderDto tradeOrderDto) {

cancel方法:

  1. public void cancelRecord(RedPacketTradeOrderDto tradeOrderDto) {

4.4 调用远程Tcc服务

调用远程Tcc服务,将远程Tcc服务参与到本地Tcc事务中,本地的服务方法也需要声明为Tcc服务,声明方式与非隐式传参方式一样,有三个约束:

  1. 在服务方法上加上@Compensable注解,并设置注解属性
  2. 服务方法的入参都须能序列化(实现Serializable接口)
  3. try方法、confirm方法和cancel方法入参类型须一样

本地服务通过远程tcc服务提供的client来调用,与非隐式传参方式不一样,无需要将这些tcc服务的client显示地声明为可加入到TCC事务中。

4.4.1 在tcc-transaction-dubbo-order中调用远程Tcc服务示例:

try方法:

  1. @Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment")
  2. public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
  3. System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
  4. order.pay(redPacketPayAmount, capitalPayAmount);
  5. orderRepository.updateOrder(order);
  6. String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
  7. String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
  8. }

confirm方法:

  1. public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

cancel方法:

  1. public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

转载来源:https://github.com/changmingxie/tcc-transaction/wiki/%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%971.2.x

参考来源:https://blog.csdn.net/yongyou890410/article/details/82719062