TCC Demo 代码实现

TCC Demo 代码实现


简介

    设计实现一个 TCC 分布式事务框架的简单 Demo,实现事务管理器,不需要实现全局事务的持久化和恢复、高可用等

工程运行

    需要MySQL数据库,保存全局事务信息,相关TCC步骤都会打印在控制台上

  • 1:启动MySQL,创建一个数据库 test
  • 2.运行当前工程的:TccDemoApplication,启动以后自动创建数据库的表
  • 3.访问:http://localhost:8080/transaction/commit,confirm示例
  • 4.访问:http://localhost:8080/transaction/cancel,cancel示例

大致实现思路

  • 1.初始化:想事务管理器注册新事务,生成全局事务唯一ID
  • 2.try阶段执行:try相关的代码执行,期间注册相应的调用记录,发送try执行结果到事务管理器,执行成功由事务管理器执行confirm或者cancel步骤
  • 3.confirm阶段:事务管理器收到try执行成功信息,根据事务ID,进入事务confirm阶段执行,confirm失败进入cancel,成功则结束
  • 4.cancel阶段:事务管理器收到try执行失败或者confirm执行失败,根据事务ID,进入cancel阶段执行后结束,如果失败了,打印日志或者告警,让人工参与处理

前置知识

TCC 原理

    TCC分布式事务主要的三个阶段:

  • 1.Try:主要是对业务系统做检测及资源预留
  • 2.Confirm:确认执行业务操作
  • 3.Cancel:取消执行业务操作

    下面以一个例子来说明三个阶段需要做的事:比如现在有两个数据库,一个用户账户数据库、一个商品库存数据库,现在提供一个买货的接口,当买卖成功时,扣除用户账户和商品库存,大致伪代码如下:

public void buy() {
   
    // 用户账户操作
    userAccount();
    // 商品账户操作
    StoreAccount();
}

    在上面这个操作做,两个函数的操作必须同时成功,不然就会出现数据不一致问题,也就是需要保证事务原子性。

    因为设定的场景是数据在两个不同的数据库,所有没有办法利用单个数据库的事务机制,它是跨数据库的,所以需要分布式事务的机制。

    下面简单模拟下,在不使用TCC事务管理器,按照TCC的思想,在代码中如何保证事务原子性

TCC 无事务管理器 Demo 伪代码

    使用上面的场景,代码大致如下:

class Demo {
   
    
    public void buy() {
   
        // try 阶段:比如去判断用户和商品的余额和存款是否充足,进行预扣款和预减库存
        if (!userServer.tryDeductAccount()) {
   
            // 用户预扣款失败,相关数据没有改变,返回错误即可
        }
        if (!storeService.tryDeductAccount()) {
   
            // cancel 阶段: 商品预减库存失败,因为前面进行了用户预扣款,所以需要进入cancel阶段,恢复用户账户
            userService.cancelDeductAccount();
        }

        // Confirm 阶段:try 成功就进行confirm阶段,这部分操作比如是将扣款成功状态和减库存状态设置为完成
        if (!userService.confirmDeductAccount() || !storeService.confirmDeductAccount()) {
   
            // cancel 阶段:confirm的任意阶段失败了,需要进行数据恢复(回滚)
            userService.cancelDeductAccount();
            storeService.cancelDeductAccount();
        }
    }
}

    上面就是一个TCC事务大致代码,可以看到:之前的每个函数操作都需要分为三个子函数,try、confirm、cancel。将其细化,在代码中判断执行,保证其事务原子性。

    上面是两个服务,用户账户和商品存储操作,看着写起来不是太多,但如果是多个服务呢?try阶段就会多很多的if,还有相应的cancel的动态增加,confirm也是,大致如下:

class Demo {
   
    
    public void buy() {
   
        // try 阶段:比如去判断用户和商品的余额和存款是否充足,进行预扣款和预减库存
        if (!userServer.tryDeductAccount()) {
   
            // 用户预扣款失败,相关数据没有改变,返回错误即可
        }
        if (!storeService.tryDeductAccount()) {
   
            // cancel 阶段: 商品预减库存失败,因为前面进行了用户预扣款,所以需要进入cancel阶段,恢复用户账户
            userService.cancelDeductAccount();
        }
        // try增加、cancel也动态增加
        if (!xxxService.tryDeductAccount()) {
   
            xxxService.cancelDeductAccount();
            xxxService.cancelDeductAccount();
        }
        if (!xxxService.tryDeductAccount()) {
   
            xxxService.cancelDeductAccount();
            xxxService.cancelDeductAccount();
            xxxService.cancelDeductAccount();
        }
        ........

        // Confirm 阶段:try 成功就进行confirm阶段,这部分操作比如是将扣款成功状态和减库存状态设置为完成
        if (!userService.confirmDeductAccount() || !storeService.confirmDeductAccount() || ......) {
   
            // cancel 阶段:confirm的任意阶段失败了,需要进行数据恢复(回滚)
            userService.cancelDeductAccount();
            storeService.cancelDeductAccount();
            .......
        }
    }
}

    可以看出代码相似性很多,工程中相似的需要分布式调用的有很多,这样的话,大量这样的类似代码就会充斥在工程中,为了偷懒,引入TCC事务管理器就能简化很多

TCC 事务管理器

    为了偷懒,用事务管理器,那偷的是哪部分懒呢?在之前的代码中,try阶段还是交给本地程序去做,而confirm和cancel委托给了事务管理器。下面看下Seata和Hmily的TCC伪代码:

interface UserService {
   

    @TCCAction(name = "userAccount", confirmMethod = "confirm", cancelMethod = "cancel")
    public void try();

    public void confirm();

    public void cancel();
}

interface 
  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个使用Spring Boot和Spring Cloud Alibaba Seata框架实现TCC模式代码模板: 1. 创建一个Spring Boot项目,添加依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> </dependency> ``` 2. 配置Seata相关信息: ```yaml spring: application: name: tcc-demo seata: enabled: true application-id: ${spring.application.name} tx-service-group: my_tx_group service: vgroup-mapping.my_tx_group: default config: type: nacos nacos: server-addr: localhost:8848 namespace: public registry: type: nacos nacos: server-addr: localhost:8848 namespace: public storage: type: db db: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root ``` 3. 创建一个TCC接口: ```java public interface TccService { @TccTransaction(confirmMethod = "confirm", cancelMethod = "cancel") boolean tryMethod(String businessKey, String param); boolean confirm(String businessKey, String param); boolean cancel(String businessKey, String param); } ``` 4. 实现TCC接口: ```java @Service public class TccServiceImpl implements TccService { @Resource private TccMapper tccMapper; @Override public boolean tryMethod(String businessKey, String param) { TccEntity tccEntity = new TccEntity(); tccEntity.setBusinessKey(businessKey); tccEntity.setParam(param); return tccMapper.insert(tccEntity) > 0; } @Override public boolean confirm(String businessKey, String param) { return true; } @Override public boolean cancel(String businessKey, String param) { return tccMapper.deleteByBusinessKey(businessKey) > 0; } } ``` 5. 配置TCC事务管理器: ```java @Configuration public class SeataConfiguration { @Bean public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner("tcc-demo", "my_tx_group"); } @Bean public TccTransactionAspect tccTransactionAspect() { return new TccTransactionAspect(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } } ``` 6. 测试TCC接口: ```java @RestController @RequestMapping("/tcc") public class TccController { @Resource private TccService tccService; @PostMapping("/try") public boolean tryMethod(@RequestParam String businessKey, @RequestParam String param) { return tccService.tryMethod(businessKey, param); } } ``` 以上是一个简单的TCC模式代码模板,可以根据实际业务需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值