分布式事务TCC模式

TCC(Try/Confirm/Cancel)编程模式的核心思想是:针对每个分支事务操作,都要向全局事务发起方注册Try、Confirm和Cancel三个操作,具体这些操作由我们自己根据业务进行实现,然后分为两个阶段去执行:

  1. Try 阶段主要是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm 一起才能真正构成一个完整的业务逻辑。
  2. Confirm 阶段主要是做确认提交,Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
  3. Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。

在这里插入图片描述

TCC框架

目前市面上的TCC框架众多比如下面这几种:

框架名称Gitbub地址
tcc-transactionhttps://github.com/changmingxie/tcc-transaction
Hmilyhttps://github.com/yu199195/hmily
ByteTCChttps://github.com/liuyangming/ByteTCC
EasyTransactionhttps://github.com/QNJR-GROUP/EasyTransaction

​ Hmily是一个高性能分布式事务tcc开源框架。基于java语言来开发(JDK1.8),支持dubbo,springcloud,motan等rpc框架进行分布式事务。它目前支持以下特性:

  • 支持嵌套事务(Nested transaction support)。
  • 采用disruptor框架进行事务日志的异步读写,与RPC框架的性能毫无差别。
  • 支持SpringBoot-starter 项目启动,使用简单。
  • RPC框架支持 : dubbo,motan,springcloud。
  • 本地事务存储支持 : redis,mongodb,zookeeper,file,mysql。
  • 事务日志序列化支持 :java,hessian,kryo,protostuff。
  • 采用Aspect AOP 切面思想与Spring无缝集成,天然支持集群。
  • RPC事务恢复,超时异常恢复等。

Hmily利用AOP对参与分布式事务的本地方法与远程方法进行拦截处理,通过多方拦截,事务参与者能透明的调用到另一方的Try、Conform、Cancel方法;传递事务上下文;并记录事务日志,酌情进行补偿,重试等。

Hmily不需要事务协调服务,但需要提供一个数据库(mysql/mongodb/zookeeper/redis/file)来进行日志存储。Hmily实现的TCC服务与普通的服务一样,只需要暴露一个接口,也就是它的Try业务。Confirm/Cancel业务逻辑,只是因为全局事务提交/回滚的需要才提供的,因此Confirm/Cancel业务只需要被Hmily事务框架发现即可,不需要被调用它的其他业务服务所感知。

官网介绍:https://dromara.org/website/zh-cn/docs/hmily/index.html

Hmily快速入门

业务说明
本案例通过hmily框架实现TCC分布式事务,模拟两个账户的转账交易过程。两个账户分别在不同的银行(张三在bank1、李四在bank2),bank1、bank2是两个微服务。对于交易过程中的每个操作,要么都成功,要么都失败。

在这里插入图片描述
数据库
创建bank1库,并导入以下表结构和数据:

CREATE DATABASE `bank1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
USE bank1;
DROP TABLE IF EXISTS `account_info`;
CREATE TABLE `account_info`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名',
  `account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号',
  `account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码',
  `account_balance` double NULL DEFAULT NULL COMMENT '帐户余额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
INSERT INTO `account_info` VALUES (1, '张三', '1', '', 10000);

创建bank2库,并导入以下表结构和数据:

CREATE DATABASE `bank2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
USE bank2;
DROP TABLE IF EXISTS `account_info`;
CREATE TABLE `account_info`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名',
  `account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号',
  `account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码',
  `account_balance` double NULL DEFAULT NULL COMMENT '帐户余额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
INSERT INTO `account_info` VALUES (2, '李四', '2', NULL, 0);

Hmily用来存储日志的数据表由它自动创建

演示案例代码

发起者
application.yml配置(只显示hmily部分)

org:
  dromara:
    hmily :
      serializer : kryo #序列化工具
      retryMax : 30 #最大重试次数
      repositorySupport : db #持久化方式
      started: true #事务发起方
      hmilyDbConfig :
        driverClassName  : com.mysql.jdbc.Driver
        url :  jdbc:mysql://localhost:3306/bank1?useUnicode=true
        username : root
        password : 123

Hmily配置类

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class DatabaseConfiguration {
    @Autowired
    private Environment env;

    @Bean
    public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){
        HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService);
        hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer"));
        hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax")));
        hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport"));
        hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started")));
        HmilyDbConfig hmilyDbConfig = new HmilyDbConfig();
        hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName"));
        hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url"));
        hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username"));
        hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password"));
        hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig);
        return hmilyTransactionBootstrap;
    }
}

feign代理

@FeignClient(value = "hmily-demo-bank2")
public interface Bank2Client {
    @GetMapping("/bank2/transfer")
    @Hmily
    Boolean transfer(@RequestParam("amount") Double amount);
}

业务
注意:Try、Confirm、cancel的方法参数必须保持一致。

@Service
public class AccountInfoTccImpl implements AccountInfoTcc {

    @Autowired
    private AccountInfoDao accountInfoDao;

    @Autowired
    private Bank2Client bank2Client;

    @Override
    @Hmily(confirmMethod = "commit", cancelMethod = "rollback")
    public void prepare( String accountNo, double amount) {
        System.out.println("...Bank1 Service  prepare..." );
        if(!bank2Client.transfer(amount)){
            throw new RuntimeException("bank2 exception");
        }
    }

    @Override
    public void commit( String accountNo, double amount) {
        System.out.println("...Bank1 Service commit..." );
    }

    @Override
    public void rollback( String accountNo, double amount) {
        accountInfoDao.updateAccountBalance(accountNo ,amount );
        System.out.println("...Bank1 Service rollback..." );
    }
}

启动类

@SpringBootApplication(exclude = MongoAutoConfiguration.class,scanBasePackages = {"org.dromara.hmily","cn.itcast.tcc.account"})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"cn.itcast.tcc.hmilydemo.bank1.feignClient"})
public class Bank1HmilyServer {
   public static void main(String[] args) {
      SpringApplication.run(Bank1HmilyServer.class, args);
   }
}

参与者

org:
  dromara:
    hmily :
      serializer : kryo #序列化工具
      retryMax : 30 #最大重试次数
      repositorySupport : db #持久化方式
      started: false #事务参与方
      hmilyDbConfig :
        driverClassName  : com.mysql.jdbc.Driver
        url :  jdbc:mysql://localhost:3306/bank2?useUnicode=true
        username : root
        password : 123

业务

@Service
public class AccountInfoServiceImpl implements AccountInfoService {
   @Autowired
   private AccountInfoDao accountInfoDao;

   @Override
   @Hmily(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod")
   public Boolean updateAccountBalance(String accountNo, Double amount) {
      System.out.println("...Bank2 Service Begin ...");
      try{
         accountInfoDao.updateAccountBalance(accountNo ,amount);
      }catch(Exception e){
         e.printStackTrace();
         throw new RuntimeException( e.getMessage() );
      }
      return true;
   }
   public Boolean confirmMethod(String accountNo, Double amount) {
      System.out.println("...Bank2 Service commit..." );
      return true;
   }

   public Boolean cancelMethod(String accountNo, Double amount) {
      accountInfoDao.updateAccountBalance(accountNo ,amount * -1);
      System.out.println("...Bank2 Service rollback..." );
      return true;
   }
}

Hmily环境搭建

新建数据库tcc_undo_log,此库用来存储hmily事务日志,空库即可,由hmily自动建表。

CREATE DATABASE `tcc_undo_log` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

Maven环境

<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>hmily-springcloud</artifactId>
    <version>2.0.5-RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </exclusion>
        <exclusion>
            <artifactId>logback-core</artifactId>
            <groupId>ch.qos.logback</groupId>
        </exclusion>
        <exclusion>
            <artifactId>slf4j-api</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
        <exclusion>
            <artifactId>logback-classic</artifactId>
            <groupId>ch.qos.logback</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>4.0.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

yaml文件

org.dromara.hmily.serializer = kryo
org.dromara.hmily.retryMax = 30
org.dromara.hmily.repositorySupport = db
org.dromara.hmily.started = true #事务发起方  false为参与方(被调用)
org.dromara.hmily.hmilyDbConfig.driverClassName = com.mysql.cj.jdbc.Driver
org.dromara.hmily.hmilyDbConfig.url = jdbc:mysql://localhost:3306/tcc_undo_log?useUnicode=true
org.dromara.hmily.hmilyDbConfig.username = root
org.dromara.hmily.hmilyDbConfig.password = 123

新建配置文件

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class HmilyConfig {

    @Autowired
    private Environment env;

    @Bean
    public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){
        HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService);
        hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer"));
        hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax")));
        hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport"));
        hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started")));
        HmilyDbConfig hmilyDbConfig = new HmilyDbConfig();
        hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName"));
        hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url"));
        hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username"));
        hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password"));
        hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig);
        return hmilyTransactionBootstrap;
    }
}

启动类上增加org.dromara.hmily的扫描项

@SpringBootApplication(exclude = MongoAutoConfiguration.class,scanBasePackages = {"org.dromara.hmily","cn.itcast.wanxinp2p.account"})

Feign代理(远程接口)中,controller的方法中要增加@Hmily注解

案例

@FeignClient(value="account-service")//服务名
public interface AccountApiAgent {
   @PostMapping(value = "/account/tcc/accounts")
   @Hmily
   RestResponse<AccountDTO> register(@RequestBody AccountRegisterDTO dto);
}
@Override
@Hmily(confirmMethod = "confirmRegister", cancelMethod = "cancelRegister")
public void register(ConsumerRegisterDTO consumerRegisterDTO) {
     ... ...
}
public void confirmRegister(ConsumerRegisterDTO consumerRegisterDTO) {
	log.info("execute confirmRegister");
}
public void cancelRegister(ConsumerRegisterDTO consumerRegisterDTO) {
	log.info("execute cancelRegister");
	remove(Wrappers.<Consumer>lambdaQuery().eq(Consumer::getMobile, 
                                               consumerRegisterDTO.getMobile()));
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCC-Transaction是一个开源的TCC补偿性分布式事务框架。TCC是Try、Confirm、Cancel的缩写,表示事务的尝试、确认和取消阶段。TCC能够对分布式事务中的各个资源进行分别锁定、提交和释放。它的优点是能够实现严格一致性并且具有较短的执行时间和高实时性要求。同时,TCC也具有一定的缺点,例如对应用的侵入性较强,需要实现每个分支的try、confirm和cancel操作,实现难度较大。 关于Spring Cloud分布式事务TCC,可以使用TCC-Transaction框架来实现。TCC-Transaction可以作为可靠性事件投递的替代品,并作为Spring Cloud Stream或Spring Cloud Bus的基础组件。此外,TCC还需要在事务管理器(协调器)节点上以高可用集群方式部署,并使用多数派算法来避免集群发生脑裂问题。 在实际应用中,TCC适用于一些需要严格一致性、执行时间短和实时性要求高的场景,例如红包和收付款业务。 更多关于TCC-Transaction框架的详细信息可以在其GitHub地址(https://github.com/changmingxie/tcc-transaction)和项目指南地址(https://github.com/changmingxie/tcc-transaction/wiki/使用指南1.2.x)中找到。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SpringCloud(6) 分布式事务【概念、常见框架选择 - tx-lcn】](https://blog.csdn.net/qq_38225558/article/details/86103229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [Spring Cloud综合实战 - 基于TCC补偿模式分布式事务](https://blog.csdn.net/Solarison/article/details/68061157)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值