Seata1.3.0之AT-TCC模式实战

分布式事务–SEATA–AT和TCC模式

seata官网:seata.io

Seata把一个分布式事务理解成一个包含了若干分支事务全局事务。全局事务的职责是协调其下管辖的分支事务

达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据库的本地事务,下

图是全局事务与分支事务的关系图:
在这里插入图片描述

Seata定义了三个组件来处理分布式事务:

Transaction Coordinator (TC): 事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运

行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各各分支事务的提交或回滚。

Transaction Manager ™: 事务管理器,TM需要嵌入应用程序中工作,它负责开启一个全局事务,并最终

向TC发起全局提交或全局回滚的指令。

Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分

支(本地)事务的提交和回滚。

1.下载、配置并启动TC

官方教程地址:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

seata的github地址https://github.com/seata/seata/tree/1.3.0,

TC下载地址:https://github.com/seata/seata/releases

首先把seata-server的启动脚本下载下来。
在这里插入图片描述

bin目录里是启动脚本,conf目录是一些配置文件。
在这里插入图片描述

Server端存储模式(store.mode)现有file、db、redis三种(后续将引入raft,mongodb),file模式无需改动,直接启动即可。
注: file模式为单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高;

db模式为高可用模式,全局事务会话信息通过db共享,相应性能差些;

redis模式Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,需要配置合适当前场景的redis持久化配置.

接下来细说db模式:https://github.com/seata/seata/tree/1.2.0/script/ 这里有三个目录,分别是client、config-center、server。

  • client

存放client端sql脚本,参数配置

  • config-center

各个配置中心参数导入脚本,config.txt(包含server和client,原名nacos-config.txt)为通用参数文件

  • server

server端数据库脚本及各个容器配置

首先建表:全局事务会话信息由3块内容构成,全局事务–>分支事务–>全局锁,对应表global_table、branch_table、lock_table,对应的sql文件在server目录下的db文件夹中。

启动包: seata–>conf–>file.conf,修改store.mode=“db”

启动包: seata–>conf–>file.conf,修改store.db相关属性。
然后就是server的配置,有file 、nacos 、eureka、redis、zk、consul、etcd3、sofa供我们选择。

接下来以nacos讲解,server相关配置信息在config-center目录的config.txt中。其中具体的参数详解官网有介绍。 接下来我们需要把这个config拷贝到启动包的目录下并修改其中的一些配置。

在这里插入图片描述

下面是我修改完的。

在这里插入图片描述

然后修改启动包: seata–>conf–>registry.conf。下面是我修改完的

appliction是注册到nacos中的服务名。serverAddr是nacos的地址,然后是组名,命名空间等不过多赘述。

在这里插入图片描述

然后我们需要把config.txt中的内容导入到nacos中,官方提供了两个脚本。

在这里插入图片描述

windos环境下可以用git bash here。

在这里插入图片描述
在这里插入图片描述

然后我们就可以启动server了。启动包: seata–>bin–>seata-server.bat,

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这样就启动成功了。启动之前别忘了启动nacos。我使用的nacos是1.3.0版本。

2.配置客户端:

我使用的seata1.3.0,相关版本说嘛:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

下面是pom文件。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>
    <groupId>com.emrubik</groupId>
    <artifactId>AT-TCC</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modules>
        <module>order</module>
        <module>account</module>
        <module>storage</module>
    </modules>

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <version>2.2.3.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.14</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.3.2.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

以经典的,订单、库存、账户为例开发。

首先需要三个微服务。每个微服务的启动类都需要加上以下注解

@EnableDiscoveryClient
@EnableAutoDataSourceProxy //此注解是开启数据源自动代理
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

每个微服务都是用naocs作为注册配置中心。

所以需要把application.properties改为bootstrap.properties或bootstrap.yaml

下面是bootstrap.yaml 配置

#spring.application.name=provider-b-config
#spring.cloud.nacos.config.file-extension=yaml
#spring.cloud.nacos.config.server-addr=localhost:8848
#spring.cloud.alibaba.seata.tx-service-group=provider-b
spring:
  application:
    name: storage-config  #naocs配置列表显示的名字是storage-config.yaml
  cloud:
    nacos:
      config:
        file-extension: yaml  #naocs配置列表显示的名字的后缀,
        server-addr: 10.10.20.51:8848  
    alibaba:
      seata:
        tx-service-group: provider-b  #这个要全局唯一,这个是前面在config.txt中的service.vgroupMapping.my_test_tx_group=default  修改为service.vgroupMapping.provider-b=default。  

在这里插入图片描述

下面是nacos中的配置信息:

spring:
  application:
    name: storage #这个是启动服务后注册到naocs中的服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    url: jdbc:mysql://localhost:3306/storage?serverTimezone=GMT%2B8
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver
server:
  port: 7002 #服务启动端口

management:
  endpoints:
    web:
      exposure:
        include: "*"
seata: #具体参数配置请查看官网
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: provider-b
  enable-auto-data-source-proxy: true  #开启数据源自动代理
  config: #配置中心
    # 指明类型
    type: nacos
    nacos:
      server-addr: "localhost:8848"
      namespace: ""
      group: "SEATA_GROUP"
      username: ""
      password: ""
  registry:  #注册中心
    type: nacos
    nacos:
      application: "seata-server"
      serverAddr: "localhost:8848"
      group: "SEATA_GROUP"
      namespace: ""
      username: ""
      password: ""
feign:
  hystrix:
    enabled: true

配置完成之后就要进行开发了。

3.业务开发
AT
//账户mapper
@Mapper
public interface AccountMapper extends BaseMapper<Account> {

    @Update("update account set money = money - #{money} where user_name = #{userName}")
    void reduceMoney(String userName, BigDecimal money);
}

//库存mapper
 @Mapper
public interface StorageMapper extends BaseMapper<Storage> {

    @Update("update storage set commodity_count=commodity_count-#{count} where commodity_code=#{commodityCode}")
    void reduceStorage(String commodityCode, Integer count);
  }
//账户service
@Service
@Slf4j
public class AccountServiceImpl implements AccountService {

    @Resource
    private AccountMapper accountMapper;

    @Resource
    private TccAccountService tccAccountService;

    @Override
    public void reduceMoney(String userName, BigDecimal money) {
        log.info("扣减账户金额");
        accountMapper.reduceMoney(userName,money);
        if(0==money.compareTo(new BigDecimal(1000))){
            throw new RuntimeException("模拟account异常"+ RootContext.getXID());
        }
    }
}

//库存service
@Service
@Slf4j
public class StorageServiceImpl implements StorageService {

    @Resource
    private StorageMapper storageMapper;

    @Resource
    private TccStorageService tccStorageService;

    @Override
    public void reduceStorage(String commodityCode, Integer count)  {
        try {
            Thread.sleep(6000);
        }catch (Exception e){
            throw new RuntimeException("模拟超时回滚");
        }
        log.info("扣减库存");
            storageMapper.reduceStorage(commodityCode,count);

//        if(count==2){
//            throw new RuntimeException("模拟storage异常"+ RootContext.getXID());
//        }
    }
}
//订单service
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private AccountClient accountClient;

    @Resource
    private StorageClient storageClient;

    @Resource
    private TccOrderService ttcOrderService;


    /**
     * 使用了@GlobalTransactional注解要捕捉远程调用异常,如果使用了feign降级策略,还需判断远程调用是否被降级了,如果被降级了就要进行全局事务回滚。
     * 可以像下面方法抛异常让TM自动回滚,也可以使用GlobalTransactionContext.reload(RootContext.getXID()).rollback();手动回滚。
     * @param commodityCode
     * @param commodityCount
     * @param userName
     * @param money
     * @return
     */
    @Override
    @GlobalTransactional(timeoutMills = 5000,rollbackFor = Exception.class)
    public String createOrder(String commodityCode, Integer commodityCount, String userName, BigDecimal money) {
        log.info("创建订单");
        log.info("order-XID:"+ RootContext.getXID());
        OrderInfo order = new OrderInfo();
        order.setCommodityCode(commodityCode);
        order.setCommodityCount(commodityCount);
        order.setOrderCode(UUID.randomUUID().toString());
        order.setUserName(userName);
        order.setMoney(money);
        orderMapper.insert(order);
        try {
            log.info("扣减库存");
            String s = storageClient.reduceStorage(commodityCode, commodityCount);
            log.info("调用库存是否有异常"+s);
            log.info("扣减账户金额");
            accountClient.reduceMoney(userName, money);

        }catch (Exception e){
            throw new RuntimeException("微服务调用异常");
        }
        return RootContext.getXID();
    }
}

我是用订单服务调用库存和账户服务。所以需要在订单的启动类加上@EnableFeignClients(basePackages = {“com.emrubik.order.client”})

//调用账户服务的client
@FeignClient(value = "account",fallback = AccountClientFallback.class)
public interface AccountClient {

    @GetMapping("/account/reduce")
    public String reduceMoney(@RequestParam(value = "userName") String userName,@RequestParam(value = "money") BigDecimal money) ;

    @GetMapping("/account/tcc")
    public String tccReduceMoney(@RequestParam(value = "userName") String userName,@RequestParam(value = "money") BigDecimal money);
}

//调用库存服务的client
@FeignClient(value = "storage")
public interface StorageClient {

    @GetMapping("/storage/reduce")
    public String reduceStorage(@RequestParam(value = "commodityCode") String commodityCode, @RequestParam(value = "commodityCount") Integer commodityCount) throws Exception;

    @GetMapping("/storage/tcc")
    public String tccReduceStorage(@RequestParam(value = "commodityCode") String commodityCode, @RequestParam(value = "commodityCount") Integer commodityCount) throws Exception;
}

controller代码很简单就不展示了。

TCC

TCC需要我们自己实现幂等性。

public class ResultHolder {
    private static Map<Class<?>, Map<String, String>> map = new ConcurrentHashMap<Class<?>, Map<String, String>>();

    public static void setResult(Class<?> actionClass, String xid, String v) {
        Map<String, String> results = map.get(actionClass);

        if (results == null) {
            synchronized (map) {
                if (results == null) {
                    results = new ConcurrentHashMap<>();
                    map.put(actionClass, results);
                }
            }
        }

        results.put(xid, v);
    }

    public static String getResult(Class<?> actionClass, String xid) {
        Map<String, String> results = map.get(actionClass);
        if (results != null) {
            return results.get(xid);
        }

        return null;
    }

    public static void removeResult(Class<?> actionClass, String xid) {
        Map<String, String> results = map.get(actionClass);
        if (results != null) {
            results.remove(xid);
        }
    }
}

账户的mapper

    @Update("update account set money = money - #{money}, freeze_money = freeze_money + #{money} where user_name = #{userName}")
    void prepareAccount(String userName, BigDecimal money);

    @Update("update account set freeze_money = freeze_money - #{money} where user_name = #{userName}")
    void commitAccount(String userName,BigDecimal money);

    @Update("update account set money = money + #{money}, freeze_money = freeze_money - #{money} where user_name = #{userName}")
    void rollbackAccount(String userName, BigDecimal money);

账户的TCC接口和实现类

@LocalTCC
public interface TccAccountService {

    @TwoPhaseBusinessAction(name = "account",commitMethod = "commit",rollbackMethod = "rollback")
    Boolean updata(BusinessActionContext businessActionContext, @BusinessActionContextParameter(paramName = "userName") String userName,
                   @BusinessActionContextParameter(paramName="money") BigDecimal money);
    boolean commit(BusinessActionContext businessActionContext);

    boolean rollback(BusinessActionContext businessActionContext);
    
    
    
@Component
@Slf4j
public class TccAccountServiceImpl implements TccAccountService {

    @Resource
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updata(BusinessActionContext businessActionContext, String userName, BigDecimal money) {
        log.info("account准备"+businessActionContext.getXid());
        accountMapper.prepareAccount(userName,money);
        ResultHolder.setResult(getClass(),businessActionContext.getXid(),"order");
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("account提交"+businessActionContext.getXid());
        String userName = businessActionContext.getActionContext("userName").toString();
        BigDecimal money =new BigDecimal(businessActionContext.getActionContext("money").toString());
        accountMapper.commitAccount(userName,money);
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("account回滚"+businessActionContext.getXid());
        String userName = businessActionContext.getActionContext("userName").toString();
        BigDecimal money =new BigDecimal(businessActionContext.getActionContext("money").toString());
        accountMapper.rollbackAccount(userName,money);
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }
}

库存的mapper

    @Update("update storage set commodity_count=commodity_count-#{count}, freeze_count=freeze_count+#{count} where commodity_code=#{commodityCode}")
    void prepareStorage(String commodityCode, Integer count);

    @Update("update storage set freeze_count=freeze_count-#{count} where commodity_code=#{commodityCode}")
    void commitStorage(String commodityCode, Integer count);

    @Update("update storage set commodity_count=commodity_count+#{count}, freeze_count=freeze_count-#{count} where commodity_code=#{commodityCode}")
    void rollbackStorage(String commodityCode, Integer count);

库存的TCC接口和实现类

@LocalTCC
public interface TccStorageService {

    @TwoPhaseBusinessAction(name = "storage",commitMethod = "commit",rollbackMethod = "rollback")
    Boolean updata(BusinessActionContext businessActionContext, @BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,
                   @BusinessActionContextParameter(paramName="count") Integer count);
    boolean commit(BusinessActionContext businessActionContext);

    boolean rollback(BusinessActionContext businessActionContext);
}

@Component
@Slf4j
public class TccStorageServiceImpl implements TccStorageService {

    @Resource
    private StorageMapper storageMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updata(BusinessActionContext businessActionContext, String commodityCode, Integer count) {
        log.info("Storage准备阶段"+businessActionContext.getXid());
        storageMapper.prepareStorage(commodityCode,count);
        ResultHolder.setResult(getClass(),businessActionContext.getXid(),"storage");
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("Storage提交"+businessActionContext.getXid());
        String commodityCode = businessActionContext.getActionContext("commodityCode").toString();
        Integer count = Integer.parseInt(businessActionContext.getActionContext("count").toString());
        storageMapper.commitStorage(commodityCode,count);
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("Storage回滚"+businessActionContext.getXid());
        String commodityCode = businessActionContext.getActionContext("commodityCode").toString();
        Integer count = Integer.parseInt(businessActionContext.getActionContext("count").toString());
        storageMapper.rollbackStorage(commodityCode,count);
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }
}

订单服务的mapper

   @Update("update order_info set state= #{state} where order_code = #{orderCode}")
    void commitOrder(String orderCode,String state);

    @Delete("delete from order_info where order_code = #{orderCode}")
    void rollbackOrder(String orderCode);

订单服务的TCC接口和实现类

@LocalTCC
public interface TccOrderService {

    @TwoPhaseBusinessAction(name = "order",commitMethod = "commit",rollbackMethod = "rollback")
    Boolean updata(BusinessActionContext businessActionContext,
                   @BusinessActionContextParameter(paramName = "orderCode")String orderCode,
                   @BusinessActionContextParameter(paramName = "commodityCode")String commodityCode,
                   @BusinessActionContextParameter(paramName = "commodityCount")Integer commodityCount,
                   @BusinessActionContextParameter(paramName = "userName") String userName,
                   @BusinessActionContextParameter(paramName = "money") BigDecimal money);
    boolean commit(BusinessActionContext businessActionContext);

    boolean rollback(BusinessActionContext businessActionContext);
}

@Component
@Slf4j
public class TccOrderServiceImpl implements TccOrderService {

    @Resource
    private OrderMapper orderMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updata(BusinessActionContext businessActionContext,String  orderCode,String commodityCode, Integer commodityCount, String userName, BigDecimal money) {
        log.info("order准备"+businessActionContext.getXid());
        OrderInfo order = new OrderInfo();
        order.setCommodityCode(commodityCode);
        order.setCommodityCount(commodityCount);
        order.setOrderCode(orderCode);
        order.setUserName(userName);
        order.setMoney(money);
        orderMapper.insert(order);
        ResultHolder.setResult(getClass(),businessActionContext.getXid(),"order");

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("order提交"+businessActionContext.getXid());
        String orderCode = businessActionContext.getActionContext("orderCode").toString();
        orderMapper.commitOrder(orderCode,"0");
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        if(ResultHolder.getResult(getClass(),businessActionContext.getXid())==null){
            return true;
        }
        log.info("order回滚"+businessActionContext.getXid());
        String orderCode = businessActionContext.getActionContext("orderCode").toString();
        orderMapper.rollbackOrder(orderCode);
        ResultHolder.removeResult(getClass(),businessActionContext.getXid());
        return true;
    }
}

然后在上面AT的service类中调用每个服务的第一阶段的方法。

    @Override
    public void tccReduceMonry(String userName, BigDecimal money) {
        log.info("扣减账户金额");
        tccAccountService.updata(null,userName,money);
        if(0==money.compareTo(new BigDecimal(1000))){
            throw new RuntimeException("模拟account异常"+ RootContext.getXID());
        }
    }

    @Override
    public void tccReduceStorage(String commodityCode, Integer count) {
//        try {
//            Thread.sleep(6000);
//        }catch (Exception e){
//            throw new RuntimeException("模拟超时回滚");
//        }

        log.info("扣减库存");
        tccStorageService.updata(null,commodityCode,count);

                if(count==2){
            throw new RuntimeException("模拟storage异常"+ RootContext.getXID());
        }
    }


 @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public void tccCreateOrder(String commodityCode, Integer commodityCount, String userName, BigDecimal money) {
        log.info("创建订单");
        log.info("order-XID:"+ RootContext.getXID());
 ttcOrderService.updata(null,UUID.randomUUID().toString(),commodityCode,commodityCount,userName,money);
        try {
            log.info("扣减库存");
            storageClient.tccReduceStorage(commodityCode,commodityCount);
            log.info("扣减账户金额");
            String s = accountClient.tccReduceMoney(userName, money);
            if(s.equals("false")){
                throw new RuntimeException("account失败");
            }

        }catch (Exception e){
            throw new RuntimeException("微服务调用异常");
        }

        if(commodityCount==3){
            throw new RuntimeException("模拟order异常");
        }

    }

同样就不展示controller了。

开发就到此结束了。

 commodityCount, String userName, BigDecimal money) {
        log.info("创建订单");
        log.info("order-XID:"+ RootContext.getXID());
 ttcOrderService.updata(null,UUID.randomUUID().toString(),commodityCode,commodityCount,userName,money);
        try {
            log.info("扣减库存");
            storageClient.tccReduceStorage(commodityCode,commodityCount);
            log.info("扣减账户金额");
            String s = accountClient.tccReduceMoney(userName, money);
            if(s.equals("false")){
                throw new RuntimeException("account失败");
            }
        }catch (Exception e){
            throw new RuntimeException("微服务调用异常");
        }
        if(commodityCount==3){
            throw new RuntimeException("模拟order异常");
        }
    }

同样就不展示controller了。

开发就到此结束了。

代码地址:https://gitee.com/sunyuntian/seata1.3.0/tree/master/AT-TCC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值