• SpringCloudAliBaba篇(七)之Seata ---> 分布式事务组件


    1、事务简介

    事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中,一个事务由一组SQL语句组成,事务具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID原则。

    • 原子性(atomicity): 事务中的操作要么都发生,要么都不发生
    • 一致性(consistency): 事务从一个一致性的状态变到另一个一致性的状态,
    • 隔离性(isolation): 事务之间不能相互干扰、相互隔离,隔离又分为四个级别: 读未提交(read uncommitted), 读已提交(read committed,解决脏读)、可重复读(rpeatable read ,解决不可重复读)、串行化(serializable 解决幻读)
    • 持久性(durability): 持久性也称为永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变是永久的,接下来的操作或故障不应该对其有任何影响。

    1.1、本地事务

    @Translation

    ​ 大多数场景下,我们的应用都只需要操作单一的数据库,这种情况的事务我们称之为本地事务(Local Transation)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:

    94

    1.2、常见的分布式事务解决方案

    • seata阿里分布式事务框架
    • 消息队列
    • saga
    • XA

    他们都有一个共同点,都是两阶段(2PC)。两阶段是指完成整个分布式事务,划分成两个步骤完成。

    这四种常见的分布式事务解决方法,分别对应着分布式事务的四种模式:AT、TCC、Sage、XA

    2、Seata是什么

    seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)

    官网:https://seata.io/Zh-cn/index.html

    源码:https://github.com/seata/seata

    官方demo:https://github.com/seata/seate-samples

    2.1、Seata的三大角色

    TC(Transaction Coordinator) - 事务协调者

    维护全局和分支事务的状态,驱动全局事务提交或回滚

    TM(Transaction Manager) - 事务管理器

    RM(Resource) - 资源管理器

    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

    其中,TC为单独部署的Server服务端,TM和RM为嵌入到应用中的Client客户端。

    2.2、AT(auto transcation)模式

    AT模式是一种无侵入的分布式事务解决方案

    阿里的seata框架,实现了该模式

    在AT模式下,用户只需关注自己的“业务SQL”,用户的“业务SQL”作为第一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作。

    95

    AT模式如何做到对业务的无侵入:

    • 一阶段

    在一阶段中,Seata会拦截“业务SQL”,首先解析SQL语义,找到“业务SQL要更新的业务数据,在业务数据被更新前,将其保存成“before image",然后执行”业务SQL“更新业务数据,在业务数据更新之后,再将其保存成”after image“,最后生成行锁,以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

    96

    • 二阶段提交

    二阶段如果是提交的话,因为业务“SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁进行删掉,完成数据清理即可。

    97

    • 二阶段回滚:

    二阶段如果是回滚的话,Seata就需要一阶段已经执行的“业务SQL”,还原业务数据,回滚方式便是用“before image"还原业务数据;但在还原前要首先要校验脏写,对比数据库当前业务数据和after image,如果两份数据一致就说明没有脏写,可以还原数据,如果不一致就说明有脏写需要人工干预处理。

    98

    2.3、TCC模式

    • 侵入性强,并且得自己实现相关事务控制逻辑
    • 整个过程中基本没有锁,性能更强

    TCC模式需要用户根据自己的业务场景实现Try,ConfirmCancel三个操作;事务发起方在一阶段执行try方式,在二阶段提交执行Contirm方法;二阶段回滚执行Cancel方法。

    99

    2.4、Saga模式

    Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

    2.5、XA模式

    前提

    • 支持XA 事务的数据库。
    • Java 应用,通过 JDBC 访问数据库。

    整体机制

    在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。

    • 执行阶段:
      • 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
      • 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
    • 完成阶段:
      • 分支提交:执行 XA 分支的 commit
      • 分支回滚:执行 XA 分支的 rollback

    3、Seata快速开始

    3.1、Seata Server(TC)环境搭建

    官方文档:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

    Server端存储模式(store.mode)支持三种:

    • file : 单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)
    • db(5.7+) :高可用模式,全局事务会话信息通过db共享,相应性能差些
    • redis : Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险;请提前配置当前场景的redis持久化配置

    3.1.2、db+Nacos方式部署

    资源下载地址: 【1.3.0版本windows为例】

    https://github.com/seata/seata/releases

    1、打开config/file.conf

    2、修改mode=“db”

    3、修改数据库连接信息(url,username,password)

    4、创建数据库(seata)

    5、下载相关需要的资源

    下载地址: https://github.com/seata/seata/tree/1.4.0

    100

    解压完成后,我们只需要要这个script文件夹即可

    101

    将这个文件夹放入我们的steta目录(方便我们用里面的一些资源)

    102

    6、引入sql,script/server/db/mysql.sql

    103

    7、打开conf/registry.conf文件进行修改

    registry部分:

    104

    config部分:

    105

    8、修改script/config-center/config.txt,为了等一下导入配置

    106

    注意点:

    配置的事务分组,要与客户端配置的事务分组保持一致

    107

    • 客户端properties配置:springcloud.alibaba.seata.tx-service-group=my_test_tx_troup
    • default需要跟客户端和registry.conf中registry中的cluster保持一致

    事务分组:异地机房停电容错机制

    my_test_tx_group可以自定义 比如(guangzhou,shanghai),对应的client也要配置

    9、配置参数同步到Nacos

    1、进入script/config-center/nacos

    108

    如果你的ip和端口都是默认的话,直接双击即可。否则可以使用下面的启动方式

    sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 93d7e8bc-389c-45e1-99a4-1b14a3309d4a
    
    • 1

    参数说明:

    -h: nacos地址

    -p: nacos端口号

    -g: 配置分组,默认为SEATA_GROUP

    -t: Nacos命名空间ID字段,默认为空

    在git bash里面执行命令即可

    109

    10、打开nacos进行查看,所有配置成功同步

    110

    11、启动seata

    找到seata/bin/seata-server.bat双击启动即可

    111

    所有启动参数

    参数全写作用备注
    -h–host指定在注册中心注册的ip不指定时获取当前ip,外部访问部署建议指定
    -p–port指定server启动的端口默认8091
    -m–storeMode事务日志存储方式支持file,db,redis,默认为file,注意:redis需seata-server1.3版本及以上
    -n–serverNode用户指定seata-server节点id如1,2,3默认为1
    -e–seataEnv指定seata-server运行环境如dev,test,服务启动会使用registry-dev.conf这样的配置

    例子:bin/seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e tset

    启动集群方式:

    bin/seata-server.sh -p 8091 -n 1
    
    • 1
    bin/seata-server.sh -p 8092 -n 2
    
    • 1
    bin/seata-server.sh -p 8093 -n 3
    
    • 1

    这时候我们的steata已经进来了

    112

    3.2、分布式事务代码搭建

    3.2.1、创建两个数据库

    • 订单数据库
    CREATE TABLE `order` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',
      `product_id` bigint NOT NULL COMMENT '商品id',
      `total_amount` int NOT NULL COMMENT '商品数量',
      `status` tinyint NOT NULL COMMENT '状态',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 库存数据库
    CREATE TABLE `stock` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT '库存id',
      `product_id` bigint NOT NULL COMMENT '商品id',
      `count` bigint NOT NULL COMMENT '库存数量',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    113

    3.2.2、创建几个服务模块

    • seata父模块

    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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>springcloud-alibaba</artifactId>
            <groupId>com.lili</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <packaging>pom</packaging>
        <modules>
            <module>seata_order</module>
            <module>seata_stock</module>
        </modules>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>seata</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
        <dependencies>
            <!--web场景启动器-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--mybatisPlus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.2</version>
            </dependency>
    
        </dependencies>
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • seata_order子模块

    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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>seata</artifactId>
            <groupId>com.lili</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>seata_order</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
        <dependencies>
            <!--nacos 服务  注册与发现-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--添加openfeign的依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
        </dependencies>
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    yml文件:

    server:
      port: 8888
    # 服务名称
    spring:
      application:
        name: order-seata-server
      cloud:
        nacos:
          discovery:
            server-addr: 101.34.254.160:8847
            username: nacos
            password: nacos
            namespace: public
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_order?characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    启动类

    @EnableFeignClients
    @SpringBootApplication
    public class SeataOrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(SeataOrderApplication.class,args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName("ll_order")
    public class Order implements Serializable {
    
        private static final long serialVersionUID = 1L;
        
        /**
         * 订单id
         */
        @TableId(value = "id",type = IdType.AUTO)
        private Long id;
        /**
         * 商品id
         */
        @TableField("product_id")
        private Long productId;
        /**
         * 商品数量
         */
        @TableField("total_amount")
        private Integer totalAmount;
        /**
         * 状态
         */
        @TableField("status")
        private Integer status;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    mapper层

    @Mapper
    public interface OrderMapper extends BaseMapper<Order> {
    
    }
    
    • 1
    • 2
    • 3
    • 4

    service层接口

    public interface OrderService extends IService<Order> {
    
        void addOrder();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    service层实现类

    @Service
    public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
        @Autowired
        StockFeignService stockFeignService;
        @Autowired
        OrderMapper orderMapper;
        @Override
        public void addOrder(){
            // 模拟添加订单信息
            Order order = new Order();
            order.setStatus(0);
            order.setProductId(10L);
            order.setTotalAmount(20);
            // 添加订单
            orderMapper.insert(order);
            // 减少库存
            stockFeignService.updateStock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    feign下的调用库存接口

    @FeignClient(value = "stock-seata-server",path = "/stock")
    public interface StockFeignService {
        @RequestMapping("/updateStock")
        void updateStock();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    controller层

    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Autowired
        public OrderService orderService;
    
        @RequestMapping("/addOrder")
        public void addOrder() {
             orderService.addOrder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    • seata_stock子模块

    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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>seata</artifactId>
            <groupId>com.lili</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>seata_stock</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
        <dependencies>
            <!--nacos 服务  注册与发现-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
                    <!--添加openfeign的依赖-->
            <dependency> 
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
        </dependencies>
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    yml文件:

    server:
      port: 9999
    # 服务名称
    spring:
      application:
        name: stock-seata-server
      cloud:
        nacos:
          discovery:
            server-addr: 101.34.254.160:8847
            username: nacos
            password: nacos
            namespace: public
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_stock?characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName("ll_stock")
    public class Stock implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        /**
         * 库存id
         */
    
        @TableId(value = "id", type = IdType.AUTO)
        private Long id;
        /**
         * 商品id
         */
    
        @TableField("product_id")
        private Long productId;
        /**
         * 库存数量
         */
    
        @TableField("count")
        private Long count;
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    mapper层

    @Mapper
    public interface StockMapper extends BaseMapper<Stock> {
    
    }
    
    • 1
    • 2
    • 3
    • 4

    service层接口

    public interface StockService extends IService<Stock> {
    
        void updateStock();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    service层实现类

    @Service
    public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements StockService {
        @Autowired
        StockMapper stockMapper;
    
        @Override
        public void updateStock() {
            // 模拟修改数据
            Stock stock = new Stock();
            stock.setId(1L);
            stock.setCount(900L-20L);
            stockMapper.updateById(stock);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    controller层

    @RestController
    @RequestMapping("/stock")
    public class StockController {
    
        @Autowired
        public StockService stockService;
    
    
        @RequestMapping("/updateStock")
        public void updateStock(){
             stockService .updateStock();
        }          
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.3.3、使用@Translation注解测试

    服务消费方

    @Transactional(rollbackFor = Exception.class )
    public void addOrder(){
        // 模拟添加订单信息
        Order order = new Order();
        order.setStatus(0);
        order.setProductId(10L);
        order.setTotalAmount(20);
        // 添加订单
        orderMapper.insert(order);
        // 更新库存
        stockFeignService.updateStock();
        // 测试异常
        int i= 1/0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    进行调用测试

    java.lang.ArithmeticException: / by zero
    
    • 1

    控制台正常报错,我们来看看数据库

    114

    订单表成功回滚,但是库存表却已经被改变了

    115

    3.3.4、整合seata(@GlobalTransactional)

    第一步,两个服务都需添加下列依赖

    <!--seata依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第二步:各微服务对应数据库中添加undo_log表

    CREATE TABLE `undo_log`(
    	`id` bigint(20) NOT NULL AUTO_INCREMENT,
        `branch_id` bigint(20) NOT NULL,
        `xid` varchar(100) NOT NULL,
        `context` varchar(128) NOT NULL,
        `rollback_info` longblob NOT NULL,
        `log_status` int(11) NOT NULL,
        `log_created` datetime NOT NULL,
        `log_modified` datetime NOT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第三步:

    配置事务的分组,这个要与前面设置的分组相对应(两个服务都需要配置)

    spring:
      cloud:
        alibaba:
          seata:
            tx-service-group: my_test_tx_group
    
    • 1
    • 2
    • 3
    • 4
    • 5

    配置seata的注册中心和配置中心(两个服务都需配置)

        #配置seata的注册中心,告诉seata client怎么去访问seate server(TC)
    seata:
      registry:
        type: nacos
        nacos:
          server-addr: 101.34.254.160:8847  #seate server所在的nacos服务地址
          application: seata-server #seate server 的服务名
          username: nacos
          password: nacos
          group: SEATA_GROUP # seate server 所在的组,默认就是SEATA_GROUP,没有改可以不用配置
      config: # 配置中心
        type: nacos
        nacos:
          server-addr: 101.34.254.160:8847
          username: nacos
          password: nacos
          group: SEATA_GROUP
          # 如果是默认空间则可以不用添加
          namespace: 93d7e8bc-389c-45e1-99a4-1b14a3309d4a
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最后,方法上加上@GlobalTransaction注解,重新测试

    @GlobalTransactional
    public void addOrder(){
        // 模拟添加订单信息
        Order order = new Order();
        order.setStatus(0);
        order.setProductId(10L);
        order.setTotalAmount(20);
        // 添加订单
        orderMapper.insert(order);
        // 减少库存
        stockFeignService.updateStock();
        // 测试异常
        int i= 1/0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行保存后,发现我们的数据成功回滚,分布式事务到这里已经完全配置成功了。

  • 相关阅读:
    一键PDF转Word,PP-Structurev2文档分析模型深度解读!
    YOLOv5从训练到移植
    非目标代谢组学(untargeted metabolomics)中常用的方法学考察的方法(四)
    保洁实业如何使用虚拟机器人提高安全性
    leakCanary原理
    2.10 PE结构:重建重定位表结构
    公共组件封装之:邮件导出弹框
    【python】(八)python类型注解
    架构师进阶,微服务设计与治理的16条常用原则
    如果有10个词,我想从中取3个词,然后把所有的10选3的可能统计记录下来,该怎么做?...
  • 原文地址:https://blog.csdn.net/qijing19991210/article/details/125548862