Seata TCC模式实战(下)

简介: Seata TCC模式实战(下)


五、案例实战


1、业务说明

99.png

业务说明:


1、用户向Order服务发起下订单的请求;

2、Order服务收到请求后,开始创建订单;

3、Order服务向Storage库存服务发起请求,减去商品库存;

4、Order服务向Account账户服务发起请求,减少账户余额;

5、全部执行成功,则成功创建订单。


2、项目结构

98.png

说明:

1、account工程是账户服务,用户管理账户余额。

2、db-init工程用户初始化项目表结构和数据。

3、easy-id-generator工程用来生成全局唯一的订单id,用来控制创建订单方法的幂等性。

4、eureka-server工程是采用eureka作为注册中心。

5、order工程是订单服务,用来新建订单。

6、order-parent是整合微服务项目的公共父依赖,类似spring-boot-starter-parent。

7、storage工程是库存服务,用来管理商品库存。


3、maven依赖

spring cloud和spring boot的版本对应关系:

97.png


spring cloud、spring boot、spring cloud Alibaba(spring-cloud-starter-alibaba-seata)三者的版本对应关系。

96.png

和seata之间的版本对应关系:官方地址

95.png

注意:

spring-cloud-starter-alibaba-seata归属与Spring Cloud Alibaba体系,两者版本保持一致。


版本说明:

spring-cloud-starter-alibaba-seata 2.1.0内嵌seata-all 0.7.1,2.1.1内嵌seata-all 0.9.0,2.2.0内嵌seata-spring-boot-starter 1.0.0, 2.2.1内嵌seata-spring-boot-starter 1.1.0。


其中seata-spring-boot-starter的核心是包含一个对应的seata-all依赖,两者的版本保持一致。


引用的时候主要需要保证spring-cloud-starter-alibaba-seata和当前项目的spring cloud的版本保持一致,

seata的版本可以通过exclusion排除默认依赖后,升级成较新的依赖。这样就做到了spring cloud版本和seate版本的解耦。


spring-cloud-starter-alibaba-seata推荐依赖配置方式

<dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>最新版</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <version>2.2.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


说明:

spring-cloud-starter-alibaba-seata的版本选择与项目依赖的spring cloud相匹配的版本。通过exclusion排除默认依赖的seata,然后引入自己想要的seata版本,实现spring cloud的版本和seata的依赖版本解耦。

注意spring-cloud-starter-alibaba-seata 2.1.0内嵌seata-all 0.7.1,2.1.1内嵌seata-all 0.9.0,2.2.0内嵌seata-spring-boot-starter 1.0.0, 2.2.1内嵌seata-spring-boot-starter 1.1.0。


所以如果是spring-cloud-starter-alibaba-seata 2.1.x的版本,依赖的是seata-all ,所以推荐如下配置:

<dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-alibaba-seata</artifactId>
          <version>${spring-cloud-alibaba-seata.version}</version>
          <exclusions>
            <exclusion>
              <artifactId>seata-all</artifactId>
              <groupId>io.seata</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>io.seata</groupId>
          <artifactId>seata-all</artifactId>
          <version>${seata.version}</version>
        </dependency>


order-parent父级项目的核心依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>order-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>order-parent</name>
    <properties>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <druid-spring-boot-starter.version>1.1.23</druid-spring-boot-starter.version>
        <seata.version>1.3.0</seata.version>
        <spring-cloud-alibaba-seata.version>2.0.0.RELEASE</spring-cloud-alibaba-seata.version>
        <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
        <skipTests>true</skipTests>
    </properties>
     <dependencies>
         <!-- 打开 seata 依赖 -->
        <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-alibaba-seata</artifactId>
          <version>${spring-cloud-alibaba-seata.version}</version>
          <exclusions>
            <exclusion>
              <artifactId>seata-all</artifactId>
              <groupId>io.seata</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>io.seata</groupId>
          <artifactId>seata-all</artifactId>
          <version>${seata.version}</version>
        </dependency>
           ……
  </dependencies>
    <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>
        </dependencies>
    </dependencyManagement>


94.png


4、属性配置

(1)、conf文件方式

针对spring-cloud-starter-alibaba-seata 2.1.x的版本,由于依赖的是seata-all,没有整合更丰富的seata配置,所以一般采用的是属性文件 + conf配置文件的方式进行配置:

93.png

属性文件配置如下:


spring.cloud.alibaba.seata.tx-service-group=order_tx_group

说明:

通过设置spring.cloud.alibaba.seata.tx-service-group属性设置事务服务的分组名称。


file.conf和registry.conf文件都可以在seata按照文件下找到。其中file.conf用来配置网络、服务端server以及客户端的相关属性。

registry.conf用来配置seata server注册的注册中心,以及属性文件保存的配置中心。


file.conf核心配置:

service {
  #transaction service group mapping
  # order_tx_group 与 yml 中的 “tx-service-group: order_tx_group” 配置一致
  # “seata-server” 与 TC 服务器的注册名一致
  # order_tx_group名称需要和属性文件中配置的spring.cloud.alibaba.seata.tx-service-group的值相匹配
  #  vgroupMapping属性是为了配置
  vgroupMapping.order_tx_group = "seata-server"
  #only support when registry.type=file, please don't set multiple addresses
  # seata-server的名称需要和上面的vgroupMapping.order_tx_group的值相匹配。
  seata-server.grouplist = "127.0.0.1:8091"
  #degrade, current not support
  enableDegrade = false
  #disable seata
  disableGlobalTransaction = false
}


注意:

1、vgroupMapping.order_tx_group中的order_tx_group需要和属性文件中配置的spring.cloud.alibaba.seata.tx-service-group的值相匹配

2、 seata-server.grouplist中的seata-server.需要和vgroupMapping.order_tx_group的值匹配

核心的目的就是指定seata-server的服务分组名称,以及seata-server服务对应的服务器ip地址。


registry.conf核心配置:

这里由于测试,且我的seata-server服务采用的默认file方式部署的,所以这里的registry.conf也都是采用的默认的,没有做相关修改。实际使用中可以根据情况和需求,修改成自己对应的配置中心和注册中心。

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "file"
  file {
    name = "file.conf"
  }
}
config {
  # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
  type = "file"
  file {
    name = "file.conf"
  }
}


(2)、属性文件配置方式

针对spring-cloud-starter-alibaba-seata 2.2.x以后的版本,由于依赖的是seata-spring-boot-starter,整合了更丰富的seata配置,所以可以省去conf文件,将seata相关数据都在属性文件中进行配置。

92.png

application.yml属性配置:

seata:
  enabled: true
  tx-service-group: order_tx_group
  service:
    vgroup-mapping:
     order_tx_group: seata-server
    grouplist:
      seata-server: 127.0.0.1:8091


注意:

1、注意属性文件配置中map类型属性配置的对应关系。

2、注意几个属性之间的关联关系。

91.png


(3)、补充说明

事务分组说明


事务分组是什么?

1.事务分组是seata的资源逻辑,类似于服务实例。在file.conf中的my_test_tx_group就是一个事务分组。

2.通过事务分组如何找到后端集群?

首先程序中配置了事务分组(GlobalTransactionScanner 构造方法的txServiceGroup参数),程序会通过用户配置的配置中心去寻找service.vgroup_mapping.事务分组配置项,取得配置项的值就是TC集群的名称。拿到集群名称程序通过一定的前后缀+集群名称去构造服务名,各配置中心的服务名实现不同。拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表。

3.为什么这么设计,不直接取服务名?

这里多了一层获取事务分组到映射集群的配置。这样设计后,事务分组可以作为资源的逻辑隔离单位,当发生故障时可以快速failover。


关于grouplist问题说明下


1.什么时候会用到file.conf中的default.grouplist?

当registry.type=file时会用到,其他时候不读。

2.default.grouplist的值列表是否可以配置多个?

可以配置多个,配置多个意味着集群,但当store.mode=file时,会报错。原因是在file存储模式下未提供本地文件的同步,所以需要使用store.mode=db,通过db来共享TC集群间数据

3.是否推荐使用default.grouplist?

不推荐,如问题1,当registry.type=file时会用到,也就是说这里用的不是真正的注册中心,不具体服务的健康检查机制当tc不可用时无法自动剔除列表,推荐使用nacos 、eureka、redis、zk、consul、etcd3、sofa。registry.type=file或config.type=file 设计的初衷是让用户再不依赖第三方注册中心或配置中心的前提下,通过直连的方式,快速验证seata服务。


5、TCC事务代码

(1)、创建订单核心代码

@GlobalTransactional
    @Override
    public void create(Order order) {
        // 从全局唯一id发号器获得id
        Long orderId = easyIdGeneratorClient.nextId("order_business");
        order.setId(orderId);
        String xid = RootContext.getXID();
        log.info("New Transaction Begins: " + xid);
        // orderMapper.create(order);
        // 这里修改成调用 TCC 第一节端方法
        orderTccAction.prepareCreateOrder(
                null,
                order.getId(),
                order.getUserId(),
                order.getProductId(),
                order.getCount(),
                order.getMoney());
        // 修改库存
        storageClient.decrease(order.getProductId(), order.getCount());
        // 修改账户余额
        accountClient.decrease(order.getUserId(), order.getMoney());
    }


说明:

1、通过添加 @GlobalTransactional注解,开启seata全局分布式事务。

2、通过生成全局的订单id控制事务的幂等性。


(2)、OrderTccAction

@LocalTCC
public interface OrderTccAction {
    /*
    第一阶段的方法
    通过注解指定第二阶段的两个方法名
    BusinessActionContext 上下文对象,用来在两个阶段之间传递数据
    @BusinessActionContextParameter 注解的参数数据会被存入 BusinessActionContext
     */
    @TwoPhaseBusinessAction(name = "orderTccAction", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepareCreateOrder(BusinessActionContext businessActionContext,
                      @BusinessActionContextParameter(paramName = "orderId") Long orderId,
                      @BusinessActionContextParameter(paramName = "userId") Long userId,
                      @BusinessActionContextParameter(paramName = "productId") Long productId,
                      @BusinessActionContextParameter(paramName = "count") Integer count,
                      @BusinessActionContextParameter(paramName = "money") BigDecimal money);
    // 第二阶段 - 提交
    boolean commit(BusinessActionContext businessActionContext);
    // 第二阶段 - 回滚
    boolean rollback(BusinessActionContext businessActionContext);
}


说明:

1、通过在接口上添加@LocalTCC注解,声明本地TCC事务控制接口

2、通过@TwoPhaseBusinessAction注解,声明Try、Confirm、Cancel三个阶段对应的具体方法。


(3)、OrderTccActionImpl

@Component
@Slf4j
public class OrderTccActionImpl implements OrderTccAction {
    @Autowired
    private OrderMapper orderMapper;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean prepareCreateOrder(BusinessActionContext businessActionContext, Long orderId, Long userId, Long productId, Integer count, BigDecimal money) {
        log.info("创建 order 第一阶段,预留资源 - "+businessActionContext.getXid());
        //因为orderId是唯一的,不能重复执行,满足幂等性, 创建状态为0(创建中)的订单
        Order order = new Order(orderId, userId, productId, count, money, 0);
        orderMapper.create(order);
        //模拟异常
        /*   if (Math.random() < 0.9999) {
            throw new RuntimeException("模拟try阶段出现 异常");
        }*/
        //事务成功,保存一个标识,供第二阶段进行判断
        ResultHolder.setResult(getClass(), businessActionContext.getXid(), "p");
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        log.info("创建 order 第二阶段提交,修改订单状态1 - "+businessActionContext.getXid());
        // 防止幂等性,如果commit阶段重复执行则直接返回
        if (ResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            return true;
        }
        //Long orderId = (Long) businessActionContext.getActionContext("orderId");
        long orderId = Long.parseLong(businessActionContext.getActionContext("orderId").toString());
        //确认提交,将订单状态修改为1(创建完成)
        orderMapper.updateStatus(orderId, 1);
        //提交成功是删除标识
        ResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        log.info("创建 order 第二阶段回滚,删除订单 - "+businessActionContext.getXid());
        //第一阶段没有完成的情况下,不必执行回滚(空回滚处理)
        //因为第一阶段有本地事务,事务失败时已经进行了回滚。
        //如果这里第一阶段成功,而其他全局事务参与者失败,这里会执行回滚
        //幂等性控制:如果重复执行回滚则直接返回
        if (ResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            return true;
        }
        //创建识别,执行Cancel操作,删除临时订单
        //Long orderId = (Long) businessActionContext.getActionContext("orderId");
        long orderId = Long.parseLong(businessActionContext.getActionContext("orderId").toString());
        orderMapper.deleteById(orderId);
        //回滚结束时,删除标识
        ResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }
}


说明:

注意TCC具体的方法实现中,对幂等、空回滚、悬挂等问题的解决。

核心逻辑解说:


1.prepareCreateOrder方法对应try阶段,会根据唯一订单号创建临时状态订单,并在当前类下注入事务id

2.commit对应confirm阶段,首先查看当前类是否和事务ID(businessActionContext.getXid())有关联,没有关联就直接返回true,不进行真实的提交逻辑。如果关联了事务id,则修改订单状态为已完成,并移除关联的事务ID。

3.rollback方法对应Cancel阶段,也是首先判断当前类下是否与businessActionContext.getXid()相关联,没有关联就直接返回true防止出现空回滚。如果有关联,则执行回滚操作,根据订单id删除临时状态的订单记录。

4.注意TCC的3个实现方法都添加了@Transactional注解开启了事务控制,保证本地分支事务的ACID特性。


(4)、订单表说明

CREATE TABLE `order` (
  `id` bigint(11) NOT NULL,
  `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
  `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
  `count` int(11) DEFAULT NULL COMMENT '数量',
  `money` decimal(11,0) DEFAULT NULL COMMENT '金额',
  `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


说明:

在TCC模式下,由于需要资源预留,一般都会在事务参与的表中添加一个资源预留字段。

在订单表中的呈现是新增了订单状态字段,记录创建中状态的订单。


(5)、order服务项目结构

90.png

说明:

1、远程调用都采用feign调用,统一存放在feign目录下

2、tcc相关的接口,统一放在tcc目录下

3、由于order服务采用的是spring-cloud-starter-alibaba-seata 2.2.1.RELEASE版本,seata-spring-boot-starter为1.4.1版本,order中采用的是属性配置项设置的seata相关属性,省略了file.conf和registry.conf文件。


(6)、account服务核心代码

@Component
@Slf4j
public class AccountTccActionImpl implements AccountTccAction {
    @Autowired
    private AccountMapper accountMapper;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean prepareDecreaseAccount(BusinessActionContext businessActionContext, Long userId, BigDecimal money) {
        log.info("减少账户金额,第一阶段锁定金额,userId="+userId+", money="+money);
        //剩余可用金额
        Account account = accountMapper.selectById(userId);
        if (account.getResidue().compareTo(money) < 0) {
            throw new RuntimeException("账户金额不足");
        }
        /*
         * 冻结可用金额
        余额-money
        冻结+money
         */
        accountMapper.updateFrozen(userId, account.getResidue().subtract(money), account.getFrozen().add(money));
        //模拟异常
        if (Math.random() < 0.3) {
            throw new RuntimeException("模拟异常");
        }
        //保存标识
        ResultHolder.setResult(getClass(), businessActionContext.getXid(), "p");
        return true;
    }
    /**
     * Confirm 方法一定要在 Try 方法之后执行。因此,Confirm 方法只需要关注重复提交的问题。
     * @param businessActionContext
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        BigDecimal money =  new BigDecimal(businessActionContext.getActionContext("money").toString());
        log.info("减少账户金额,第二阶段,提交,userId="+userId+", money="+money);
        //防止重复提交,确认try方法已经执行
        if (ResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            return true;
        }
        accountMapper.updateFrozenToUsed(userId, money);
        //删除标识
        ResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        BigDecimal money =  new BigDecimal(businessActionContext.getActionContext("money").toString());
        //防止重复提交,确保try方法已经执行,防止空回滚
        if (ResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            return true;
        }
        log.info("减少账户金额,第二阶段,回滚,userId="+userId+", money="+money);
        accountMapper.updateFrozenToResidue(userId, money);
        //删除标识
        ResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }
}


说明:

账户金额扣减TCC逻辑的3个阶段说明


1.prepareDecreaseAccount方法对应Try阶段,主要是执行资源预留,将要扣减的金额先保留到冻结金额中。

2.commit方法对应Confirm阶段,事务提交过程,主要负责将冻结的金额真正扣除。

3.rollback方法对应Cancel阶段,事务回滚,主要负责将冻结的金额解冻返回到账户的余额中。


(7)、account表

CREATE TABLE `account` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
  `total` decimal(10,0) DEFAULT NULL COMMENT '总额度',
  `used` decimal(10,0) DEFAULT NULL COMMENT '已用余额',
  `residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用额度',
  `frozen` decimal(10,0) DEFAULT '0' COMMENT 'TCC事务锁定的金额',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;


说明:

主要是新增了frozen字段,用来保存暂时冻结的金额。


6、测试

1.先启动db-init服务,初始化数据库

2.再启动eureka-server服务,作为注册中心

3.然后启动其他服务。

85.png

发起创建订单请求:http://localhost:8083/create?userId=1&productId=1&count=10&money=100

83.png

84.png

7、异常模拟

分别在TCC的各个阶段添加模拟的异常,看程序的执行情况。

//模拟try阶段异常
    if (Math.random() < 0.9999) {
        throw new RuntimeException("模拟try阶段出现异常");
    }
    if (Math.random() < 0.9999) {
        throw new RuntimeException("模拟commit阶段异常");
    }
    if (Math.random() < 0.9999) {
        throw new RuntimeException("模拟cancel阶段异常");
    }


说明:


事务的执行顺序是:创建订单——》修改库存——》减账户余额,

创建订单的try阶段出现异常,会触发创建订单TCC操作中的Cancel操作进行空回滚;

修改库存的try阶段出现异常,会触发创建订单TCC操作中的Cancel操作以及修改库存TCC操作中的Cancel操作进行空回滚;

减账户余额的try阶段出现异常,会触发创建订单TCC操作中的Cancel操作以及扣减库存的TCC操作中的Cancel操作,减账户余额的TCC操作中的Cancel操作进行空回滚。

Confirm操作出现异常后,会不停的重试,直到执行成功。

Cancel操作出现异常后,也会不停重试,直到执行成功。

(针对这里Confirm、Cancel的操作建议加入重试次数,失败一定操作后停止,记录相关记录,后面人工介入处理)。


8、seata tcc模式实战源码

GitHub地址


总结


1、理解TCC模式的底层逻辑:核心是将一个完整的事务分成了2个阶段,一阶段是所有事务参与者RM都注册到TC事务协调器,并发起分支事务请求进行资源预留,然后主动向TC汇报执行结果。二阶段TC事务协调器会根据收到的所有RM一阶段分支事务的执行结果来判断让RM继续执行提交操作还是回滚操作。

2、知道TCC模式和AT模式差别以及相互之间的优势。

3、实现TCC需要自己实现prepare、commit、rollback方法,并考虑空回滚、幂等、悬挂等问题。

4、知道怎么使用Seata框架实现 TCC事务。

5、需要在相关表中添加字段,用来保存预留资源。


很多人对分布式事务都心存畏惧,一是工作中接触的机会少,二是网上可以参考的实际可用的案例真的太少,大多数小伙伴都只是背了下各种分布式事务的相关实现方案的理论,而没有实际实现经验。


推荐大家都自己动手实战一番,做到心中有数,遇事不慌,而不是空谈理论。

目录
相关文章
|
2月前
|
自然语言处理 监控 Dubbo
Seata常见问题之使用tcc模式配置yml如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
74 4
|
3月前
|
数据库
|
3月前
|
Java 数据库连接 API
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
61 0
|
2月前
|
NoSQL Java 数据库
Seata常见问题之xa模式下插入一条数据再更新这条数据会报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
109 2
|
2月前
|
Java 关系型数据库 微服务
Seata常见问题之项目一直启动不成功如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
57 0
|
2月前
|
存储 Java Nacos
Seata常见问题之xa模式出现错误xid is not valid如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
70 4
|
3月前
|
数据库 开发者
Seata的 TCC 模式
Seata的 TCC 模式
|
3月前
|
存储 Java Apache
Seata 的 AT 模式
Seata 的 AT 模式
|
2月前
|
Nacos 数据库
分布式事务解决方案Seata
分布式事务解决方案Seata
26 1
|
2月前
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏

热门文章

最新文章