• SpringCloud - Spring Cloud Alibaba 之 Seata分布式事务服务;Seata TC Server集群部署(二十二)


    阅读本文前可先参考:

    SpringCloud - Spring Cloud Alibaba 之 Seata分布式事务服务详解;部署(十八)_MinggeQingchun的博客-CSDN博客

    SpringCloud - Spring Cloud Alibaba 之 Seata分布式事务服务;集成Nacos配置中心(十九)_MinggeQingchun的博客-CSDN博客

    SpringCloud - Spring Cloud Alibaba 之 Seata分布式事务服务;AT事务模式(二十)_MinggeQingchun的博客-CSDN博客

    https://blog.csdn.net/MinggeQingchun/article/details/126199942

    Seata TC Server集群部署

    生产环境下,我们需要部署集群 Seata TC Server,实现高可用。

    集群时,多个 Seata TC Server 通过 db 数据库或者redis实现全局事务会话信息的共享

    每个Seata TC Server注册自己到注册中心上,应用从注册中心获得Seata TC Server实例

    Seata TC Server 集群搭建具体步骤(采用Nacos注册中心):

    1、下载并解压两份 seata-server-1.4.2.tar.gz

    2、初始化 Seata TC Server 的 db 数据库,在 MySQL 中,创建 seata 数据库,并在该库下执行如下SQL脚本:

    通过Seata官网下载1.4.2版本的 source 

    下载中心

    使用seata-1.4.2\script\server\db脚本(MySQL)

    1. -- -------------------------------- The script used when storeMode is 'db' --------------------------------
    2. -- the table to store GlobalSession data
    3. CREATE TABLE IF NOT EXISTS `global_table`
    4. (
    5. `xid` VARCHAR(128) NOT NULL,
    6. `transaction_id` BIGINT,
    7. `status` TINYINT NOT NULL,
    8. `application_id` VARCHAR(32),
    9. `transaction_service_group` VARCHAR(32),
    10. `transaction_name` VARCHAR(128),
    11. `timeout` INT,
    12. `begin_time` BIGINT,
    13. `application_data` VARCHAR(2000),
    14. `gmt_create` DATETIME,
    15. `gmt_modified` DATETIME,
    16. PRIMARY KEY (`xid`),
    17. KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
    18. KEY `idx_transaction_id` (`transaction_id`)
    19. ) ENGINE = InnoDB
    20. DEFAULT CHARSET = utf8;
    21. -- the table to store BranchSession data
    22. CREATE TABLE IF NOT EXISTS `branch_table`
    23. (
    24. `branch_id` BIGINT NOT NULL,
    25. `xid` VARCHAR(128) NOT NULL,
    26. `transaction_id` BIGINT,
    27. `resource_group_id` VARCHAR(32),
    28. `resource_id` VARCHAR(256),
    29. `branch_type` VARCHAR(8),
    30. `status` TINYINT,
    31. `client_id` VARCHAR(64),
    32. `application_data` VARCHAR(2000),
    33. `gmt_create` DATETIME(6),
    34. `gmt_modified` DATETIME(6),
    35. PRIMARY KEY (`branch_id`),
    36. KEY `idx_xid` (`xid`)
    37. ) ENGINE = InnoDB
    38. DEFAULT CHARSET = utf8;
    39. -- the table to store lock data
    40. CREATE TABLE IF NOT EXISTS `lock_table`
    41. (
    42. `row_key` VARCHAR(128) NOT NULL,
    43. `xid` VARCHAR(128),
    44. `transaction_id` BIGINT,
    45. `branch_id` BIGINT NOT NULL,
    46. `resource_id` VARCHAR(256),
    47. `table_name` VARCHAR(32),
    48. `pk` VARCHAR(36),
    49. `gmt_create` DATETIME,
    50. `gmt_modified` DATETIME,
    51. PRIMARY KEY (`row_key`),
    52. KEY `idx_branch_id` (`branch_id`)
    53. ) ENGINE = InnoDB
    54. DEFAULT CHARSET = utf8;

    3、修改 seata/conf/file.conf 配置文件,修改使用 db 数据库,实现 Seata TC Server 的全局事务会话信息的共享;

    (1)mode = "db"

    (2)数据库的连接信息

    driverClassName = "com.mysql.cj.jdbc.Driver"

    url = "jdbc:mysql://IP:3306/seata"

    user = "root"

    password = "123456"

    4、设置使用 Nacos 注册中心;

    修改 seata/conf/registry.conf 配置文件,设置使用 Nacos 注册中心;

    (1)type = "nacos"

    (2)Nacos连接信息:

      nacos {
        application = "seata-server"
        serverAddr = "127.0.0.1:8848"
        group = "SEATA_GROUP"
        namespace = ""
        cluster = "default"
        username = "nacos"
        password = "nacos"
      }

    5、启动MySQL数据库和Nacos注册中心

    6、启动两个 TC Server

    执行 ./seata-server.sh -p 18091 -n 1 命令,启动第一个TC Server;

    -p:Seata TC Server 监听的端口;

    -n:Server node,在多个 TC Server 时,需区分各自节点,用于生成不同区间的 transactionId 事务编号,以免冲突;

    执行 ./seata-server.sh -p 28091 -n 2 命令,启动第二个TC Server

    注:

    如果使用虚拟机,最好修改内存为256MB

    7、打开Nacos注册中心控制台,可以看到有两个Seata TC Server 实例

      

    8、应用测试

    一、AT事务模式:单体应用SpringBoot多数据源分布式事务

    在Spring Boot单体项目中,使用了多数据源,就要保证多个数据源的数据一致性,即产生了分布式事务的问题,采用Seata的AT事务模式来解决该分布式事务问题

    以下图购物下单为例

    1、创建数据库、表、插入数据等

    (1)accountdb账户库、account账户表

    1. SET NAMES utf8mb4;
    2. SET FOREIGN_KEY_CHECKS = 0;
    3.  
    4. -- ----------------------------
    5. -- Table structure for account
    6. -- ----------------------------
    7. DROP TABLE IF EXISTS `account`;
    8. CREATE TABLE `account`  (
    9.   `id` int(20) NOT NULL AUTO_INCREMENT,
    10.   `user_id` int(20) NULL DEFAULT NULL,
    11.   `balance` decimal(20, 0) NULL DEFAULT NULL,
    12.   `update_time` datetime(6) NULL DEFAULT NULL,
    13.   PRIMARY KEY (`id`) USING BTREE
    14. ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    15.  
    16. SET FOREIGN_KEY_CHECKS = 1;

    (2)productdb产品库、product产品表

    1. SET NAMES utf8mb4;
    2. SET FOREIGN_KEY_CHECKS = 0;
    3.  
    4. -- ----------------------------
    5. -- Table structure for product
    6. -- ----------------------------
    7. DROP TABLE IF EXISTS `product`;
    8. CREATE TABLE `product`  (
    9.   `id` int(20) NOT NULL AUTO_INCREMENT,
    10.   `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
    11.   `price` decimal(10, 2) NULL DEFAULT NULL,
    12.   `stock` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
    13.   `add_time` datetime(6) NULL DEFAULT NULL,
    14.   `update_time` datetime(6) NULL DEFAULT NULL,
    15.   PRIMARY KEY (`id`) USING BTREE
    16. ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    17.  
    18. SET FOREIGN_KEY_CHECKS = 1;

    (3)orderdb订单库、orders 订单表

    1. SET NAMES utf8mb4;
    2. SET FOREIGN_KEY_CHECKS = 0;
    3.  
    4. -- ----------------------------
    5. -- Table structure for orders
    6. -- ----------------------------
    7. DROP TABLE IF EXISTS `orders`;
    8. CREATE TABLE `orders`  (
    9.   `id` int(20) NOT NULL AUTO_INCREMENT,
    10.   `user_id` int(20) NULL DEFAULT NULL,
    11.   `product_id` int(20) NULL DEFAULT NULL,
    12.   `pay_amount` decimal(20, 0) NULL DEFAULT NULL,
    13.   `add_time` datetime(6) NULL DEFAULT NULL,
    14.   `update_time` datetime(6) NULL DEFAULT NULL,
    15.   PRIMARY KEY (`id`) USING BTREE
    16. ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    17.  
    18. SET FOREIGN_KEY_CHECKS = 1;

    (4)undo_log表 

    Seata AT 模式

    1. -- 注意此处0.7.0+ 增加字段 context
    2. CREATE TABLE `undo_log` (
    3.   `id` bigint(20) NOT NULL AUTO_INCREMENT,
    4.   `branch_id` bigint(20) NOT NULL,
    5.   `xid` varchar(100) NOT NULL,
    6.   `context` varchar(128) NOT NULL,
    7.   `rollback_info` longblob NOT NULL,
    8.   `log_status` int(11) NOT NULL,
    9.   `log_created` datetime NOT NULL,
    10.   `log_modified` datetime NOT NULL,
    11.   PRIMARY KEY (`id`),
    12.   UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    13. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

    注:

    每个库必须创建 undo_log 表,是 Seata AT模式必须创建的表,主要用于分支事务的回滚

    Seata AT 模式

      

    2、创建 SpringBoot单体应用

    1、创建一个 springboot应用,命名 springcloud-alibaba-2-seata-distributed-transaction

    2、添加依赖(非Spring CLoud 微服务项目,没有Spring CLoud依赖)

    主要添加 Nacos-client依赖

    1. <dependency>
    2. <groupId>com.alibaba.nacosgroupId>
    3. <artifactId>nacos-clientartifactId>
    4. <version>2.1.0version>
    5. dependency>
    1. <groupId>com.companygroupId>
    2. <artifactId>springcloud-alibaba-2-seata-distributed-transactionartifactId>
    3. <version>1.0.0version>
    4. <name>springcloud-alibaba-2-seata-distributed-transactionname>
    5. <description>Demo project for Spring Bootdescription>
    6. <properties>
    7. <java.version>1.8java.version>
    8. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    9. <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    10. <spring-boot.version>2.3.12.RELEASEspring-boot.version>
    11. <spring-cloud-alibaba.version>2.2.7.RELEASEspring-cloud-alibaba.version>
    12. properties>
    13. <dependencies>
    14. <dependency>
    15. <groupId>org.springframework.bootgroupId>
    16. <artifactId>spring-boot-starter-webartifactId>
    17. dependency>
    18. <dependency>
    19. <groupId>org.springframework.bootgroupId>
    20. <artifactId>spring-boot-starter-actuatorartifactId>
    21. dependency>
    22. <dependency>
    23. <groupId>org.springframework.bootgroupId>
    24. <artifactId>spring-boot-starter-testartifactId>
    25. <scope>testscope>
    26. <exclusions>
    27. <exclusion>
    28. <groupId>org.junit.vintagegroupId>
    29. <artifactId>junit-vintage-engineartifactId>
    30. exclusion>
    31. exclusions>
    32. dependency>
    33. <dependency>
    34. <groupId>org.projectlombokgroupId>
    35. <artifactId>lombokartifactId>
    36. dependency>
    37. <dependency>
    38. <groupId>mysqlgroupId>
    39. <artifactId>mysql-connector-javaartifactId>
    40. dependency>
    41. <dependency>
    42. <groupId>org.mybatis.spring.bootgroupId>
    43. <artifactId>mybatis-spring-boot-starterartifactId>
    44. <version>2.1.3version>
    45. dependency>
    46. <dependency>
    47. <groupId>io.seatagroupId>
    48. <artifactId>seata-spring-boot-starterartifactId>
    49. <version>1.3.0version>
    50. dependency>
    51. <dependency>
    52. <groupId>com.baomidougroupId>
    53. <artifactId>dynamic-datasource-spring-boot-starterartifactId>
    54. <version>3.2.0version>
    55. dependency>
    56. <dependency>
    57. <groupId>com.alibaba.nacosgroupId>
    58. <artifactId>nacos-clientartifactId>
    59. <version>2.1.0version>
    60. dependency>
    61. dependencies>
    62. <dependencyManagement>
    63. <dependencies>
    64. <dependency>
    65. <groupId>com.alibaba.cloudgroupId>
    66. <artifactId>spring-cloud-alibaba-dependenciesartifactId>
    67. <version>${spring-cloud-alibaba.version}version>
    68. <type>pomtype>
    69. <scope>importscope>
    70. dependency>
    71. <dependency>
    72. <groupId>org.springframework.cloudgroupId>
    73. <artifactId>spring-cloud-dependenciesartifactId>
    74. <version>Hoxton.SR12version>
    75. <type>pomtype>
    76. <scope>importscope>
    77. dependency>
    78. <dependency>
    79. <groupId>org.springframework.bootgroupId>
    80. <artifactId>spring-boot-dependenciesartifactId>
    81. <version>${spring-boot.version}version>
    82. <type>pomtype>
    83. <scope>importscope>
    84. dependency>
    85. dependencies>
    86. dependencyManagement>
    87. <build>
    88. <plugins>
    89. <plugin>
    90. <groupId>org.apache.maven.pluginsgroupId>
    91. <artifactId>maven-compiler-pluginartifactId>
    92. <configuration>
    93. <source>1.8source>
    94. <target>1.8target>
    95. <encoding>UTF-8encoding>
    96. configuration>
    97. plugin>
    98. <plugin>
    99. <groupId>org.springframework.bootgroupId>
    100. <artifactId>spring-boot-maven-pluginartifactId>
    101. plugin>
    102. <plugin>
    103. <groupId>org.mybatis.generatorgroupId>
    104. <artifactId>mybatis-generator-maven-pluginartifactId>
    105. <version>1.4.0version>
    106. <configuration>
    107. <configurationFile>src/main/resources/generatorConfig.xmlconfigurationFile>
    108. <verbose>trueverbose>
    109. <overwrite>trueoverwrite>
    110. configuration>
    111. plugin>
    112. plugins>
    113. <resources>
    114. <resource>
    115. <directory>src/main/javadirectory>
    116. <includes>
    117. <include>**/*.xmlinclude>
    118. includes>
    119. resource>
    120. <resource>
    121. <directory>src/main/resourcesdirectory>
    122. <includes>
    123. <include>**/*.*include>
    124. includes>
    125. resource>
    126. resources>
    127. build>

    3、application.properties配置文件

    1. #内嵌服务器端口
    2. server.port=8081
    3. #应用服务名称
    4. spring.application.name=springcloud-alibaba-2-seata-distributed-transaction
    5. # 设置默认的数据源或者数据源组,默认值即为master
    6. spring.datasource.dynamic.primary=order-ds
    7. # 订单order数据源配置
    8. spring.datasource.dynamic.datasource.order-ds.url=jdbc:mysql://localhost:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
    9. spring.datasource.dynamic.datasource.order-ds.driver-class-name=com.mysql.cj.jdbc.Driver
    10. spring.datasource.dynamic.datasource.order-ds.username=root
    11. spring.datasource.dynamic.datasource.order-ds.password=admin123456
    12. # 商品product数据源配置
    13. spring.datasource.dynamic.datasource.product-ds.url=jdbc:mysql://localhost:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
    14. spring.datasource.dynamic.datasource.product-ds.driver-class-name=com.mysql.cj.jdbc.Driver
    15. spring.datasource.dynamic.datasource.product-ds.username=root
    16. spring.datasource.dynamic.datasource.product-ds.password=admin123456
    17. # 账户account数据源配置
    18. spring.datasource.dynamic.datasource.account-ds.url=jdbc:mysql://localhost:3306/accountdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
    19. spring.datasource.dynamic.datasource.account-ds.driver-class-name=com.mysql.cj.jdbc.Driver
    20. spring.datasource.dynamic.datasource.account-ds.username=root
    21. spring.datasource.dynamic.datasource.account-ds.password=admin123456
    22. # 是否启动对Seata的集成
    23. spring.datasource.dynamic.seata=true
    24. #-----------------------------------------------------------
    25. ##单机版 tc server 配置
    26. ## Seata应用编号,默认为 ${spring.application.name}
    27. #seata.application-id=springboot-seata
    28. ## Seata事务组编号,用于TC集群名,一般格式为:${spring.application.name}-group
    29. #seata.tx-service-group=springboot-seata-group
    30. ## 虚拟组和分组的映射 seata.service.vgroup-mapping.${seata.tx-service-group}=default
    31. #seata.service.vgroup-mapping.springboot-seata-group=default
    32. ## 分组和Seata服务的映射,此处default指上面 seata.service.vgroup-mapping.springboot-seata-group 的值 default
    33. #seata.service.grouplist.default=192.168.133.129:8091
    34. ## 存储模式 默认 file模式
    35. #seata.config.type=file
    36. ## 默认为 file
    37. #seata.registry.type=file
    38. #------------------------------------------------------------
    39. #------------------------------------------------------------
    40. #集群版 tc server 配置
    41. # Seata应用编号,默认为 ${spring.application.name}
    42. seata.application-id=springboot-seata
    43. # Seata事务组编号,用于TC集群名,一般格式为:${spring.application.name}-group
    44. seata.tx-service-group=springboot-seata-group
    45. # 虚拟组和分组的映射 seata.service.vgroup-mapping.${seata.tx-service-group}=default
    46. seata.service.vgroup-mapping.springboot-seata-group=default
    47. #------------------------------------------------------------
    48. #设置使用注册中心
    49. seata.registry.type=nacos
    50. seata.registry.nacos.cluster=default
    51. seata.registry.nacos.application=seata-server
    52. seata.registry.nacos.group=SEATA_GROUP
    53. seata.registry.nacos.server-addr=192.168.133.129:8848
    54. seata.enable-auto-data-source-proxy=false
    55. seata.client.rm.lock.retry-policy-branch-rollback-on-conflict=false

    4、编写相应的 controller、model、mapper、service类,这里只给出调用顺序相关的类

     controller测试类

    1. @Slf4j //lombok
    2. @RestController
    3. public class OrderController {
    4.  
    5.     @Autowired
    6.     private OrderService orderService;
    7.  
    8.     @RequestMapping("/order")
    9.     public Integer createOrder(@RequestParam("userId") Integer userId,
    10.                                @RequestParam("productId") Integer productId) throws Exception {
    11.  
    12.         log.info("请求下单, 用户:{}, 商品:{}", userId, productId);
    13.  
    14.         return orderService.createOrder(userId, productId);
    15.     }
    16. }

    order逻辑类

    注:

    (1)@DS注解;多数据源切换

    (2)@GlobalTransactional注解;seata全局事务注解

    主服务加上@GlobalTransactional注解即可,被调用服务不用加@GlobalTransactional和@Transactional

    1. @Slf4j
    2. @Service
    3. public class OrderServiceImpl implements OrderService {
    4.  
    5.     @Autowired
    6.     private OrdersMapper ordersMapper;
    7.  
    8.     @Autowired
    9.     private AccountService accountService;
    10.  
    11.     @Autowired
    12.     private ProductService productService;
    13.  
    14.     @Override
    15.     /**
    16.      * MyBatis-Plus 使用 @DS注解 做多数据源切换
    17.      * 语法:@DS(value = "数据源名称")
    18.      * 1、依赖:
    19.      *        
    20.      *             com.baomidou
    21.      *             dynamic-datasource-spring-boot-starter
    22.      *             3.0.0
    23.      *        
    24.      * 2、yml 或 properties 配置
    25.      * # 设置默认的数据源或者数据源组,默认值即为master
    26.      * spring.datasource.dynamic.primary=order-ds
    27.      *
    28.      * # 订单order数据源配置
    29.      * spring.datasource.dynamic.datasource.order-ds.url=jdbc:mysql://localhost:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
    30.      * spring.datasource.dynamic.datasource.order-ds.driver-class-name=com.mysql.cj.jdbc.Driver
    31.      * spring.datasource.dynamic.datasource.order-ds.username=root
    32.      * spring.datasource.dynamic.datasource.order-ds.password=admin123456
    33.      *
    34.      * # 商品product数据源配置
    35.      * spring.datasource.dynamic.datasource.product-ds.url=jdbc:mysql://localhost:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
    36.      * spring.datasource.dynamic.datasource.product-ds.driver-class-name=com.mysql.cj.jdbc.Driver
    37.      * spring.datasource.dynamic.datasource.product-ds.username=root
    38.      * spring.datasource.dynamic.datasource.product-ds.password=admin123456
    39.      * 3、@DS注解到实现类或者实现类的方法上才可以
    40.      * 当注解添加到类上,意味着此类里的方法都使用此数据源;
    41.      * 当注解添加到方法上时,意味着此方法上使用的数据源优先级高于其他一切配置
    42.      * 注:
    43.      * (1)注解添加在dao.mapper上无效
    44.      * (2)注解添加到interface Service类上无效
    45.      * (3)注解添加到interface Service方法上无效
    46.      */
    47.     @DS(value = "order-ds")
    48.     @GlobalTransactional //seata全局事务注解
    49.     public Integer createOrder(Integer userId, Integer productId) throws Exception {
    50.         Integer amount = 1; // 购买数量暂时设置为 1
    51.  
    52.         log.info("当前 XID: {}", RootContext.getXID());
    53.  
    54.         //1、减库存
    55.         Product product = productService.reduceStock(productId, amount);
    56.  
    57.         //2、减余额
    58.         accountService.reduceBalance(userId, product.getPrice());
    59.  
    60.         //3、下订单
    61.         Orders order = new Orders();
    62.         order.setUserId(userId);
    63.         order.setProductId(productId);
    64.         order.setPayAmount(product.getPrice().multiply(new BigDecimal(amount)));
    65.         order.setAddTime(new Date());
    66.         ordersMapper.insertSelective(order);
    67.  
    68.         //造成异常,测试是否回滚
    69.         //int a = 10/0;
    70.  
    71.         log.info("下订单: {}", order.getId());
    72.  
    73.         // 返回订单编号
    74.         return order.getId();
    75.     }
    76. }
    77.  product逻辑类

     product逻辑类

    1. @Slf4j
    2. @Service
    3. public class ProductServiceImpl implements ProductService {
    4.  
    5.     @Autowired
    6.     private ProductMapper productMapper;
    7.  
    8.     @Override
    9.     @DS(value = "product-ds")
    10.     public Product reduceStock(Integer productId, Integer amount) throws Exception {
    11.         log.info("当前 XID: {}", RootContext.getXID());
    12.  
    13.         // 检查库存
    14.         Product product = productMapper.selectByPrimaryKey(productId);
    15.         if (product.getStock() < amount) {
    16.             throw new Exception("库存不足");
    17.         }
    18.  
    19.         // 扣减库存
    20.         int updateCount = productMapper.reduceStock(productId, amount);
    21.         // 扣除成功
    22.         if (updateCount == 0) {
    23.             throw new Exception("库存不足");
    24.         }
    25.  
    26.         //造成异常,测试是否回滚
    27.         //int a = 10/0;
    28.  
    29.         // 扣除成功
    30.         log.info("扣除 {} 库存成功", productId);
    31.  
    32.         return product;
    33.     }
    34. }

    account逻辑类

    1. @Slf4j
    2. @Service
    3. public class AccountServiceImpl implements AccountService {
    4.  
    5.     @Autowired
    6.     private AccountMapper accountMapper;
    7.  
    8.     @Override
    9.     @DS(value = "account-ds")
    10.     public void reduceBalance(Integer userId, BigDecimal money) throws Exception {
    11.         log.info("当前 XID: {}", RootContext.getXID());
    12.  
    13.         // 检查余额
    14.         Account account = accountMapper.selectAccountByUserId(userId);
    15.         if (account.getBalance().doubleValue() < money.doubleValue()) {
    16.             throw new Exception("余额不足");
    17.         }
    18.  
    19.         // 扣除余额
    20.         int updateCount = accountMapper.reduceBalance(userId, money);
    21.         // 扣除成功
    22.         if (updateCount == 0) {
    23.             throw new Exception("余额不足");
    24.         }
    25.  
    26.         //造成异常,测试是否回滚
    27.         //int a = 10/0;
    28.  
    29.         log.info("扣除用户 {} 余额成功", userId);
    30.     }
    31. }

    5、启动seata-server;浏览器输入访问 http://localhost:8081/order?userId=1&productId=1

    可分别在 OrderServiceImpl、ProductServiceImpl、AccountServiceImpl 实现类中 写入如下代码进行事务回滚测试

    1. //造成异常,测试是否回滚
    2. int a = 10/0;

    二、AT事务模式:微服务SpringCloud的分布式事务

    1、创建 4 个SpringBoot 模块

    (1)springcloud-alibaba-2-seata-distributed-commons

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.projectlombokgroupId>
    4. <artifactId>lombokartifactId>
    5. <version>1.18.16version>
    6. dependency>
    7. <dependency>
    8. <groupId>org.springframework.cloudgroupId>
    9. <artifactId>spring-cloud-starter-openfeignartifactId>
    10. <version>3.0.0version>
    11. dependency>
    12. dependencies>
    1. @FeignClient(name = "springcloud-alibaba-2-seata-distributed-account")
    2. public interface FeignAccountService {
    3. /**
    4. * 扣除余额
    5. *
    6. * @param userId 用户ID
    7. * @param money 扣减金额
    8. * @throws Exception 失败时抛出异常
    9. */
    10. @PostMapping("/account/reduceBalance")
    11. void reduceBalance(@RequestParam("userId") Integer userId, @RequestParam("money") BigDecimal money);
    12. }
    13. @FeignClient(name = "springcloud-alibaba-2-seata-distributed-order")
    14. public interface FeignOrderService {
    15. /**
    16. * 创建订单
    17. *
    18. * @param userId 用户ID
    19. * @param productId 产品ID
    20. * @return 订单编号
    21. * @throws Exception 创建订单失败,抛出异常
    22. */
    23. Integer createOrder(Integer userId, Integer productId) throws Exception;
    24. }
    25. @FeignClient(name = "springcloud-alibaba-2-seata-distributed-product")
    26. public interface FeignProductService {
    27. /**
    28. * 减库存
    29. *
    30. * @param productId 商品ID
    31. * @param amount 扣减数量
    32. * @throws Exception 扣减失败时抛出异常
    33. */
    34. @PostMapping("/product/reduceStock")
    35. Product reduceStock(@RequestParam("productId") Integer productId, @RequestParam("amount") Integer amount);
    36. }

    (2)springcloud-alibaba-2-seata-distributed-order 

    注意:

    1、异常需要层层往上抛,如果你在子服务将异常处理的话(比如全局异常处理GlobalExceptionHandler),seata会认为你已经手动处理了异常

    2、出现事务失效的情况下,优先检查 RootContext.getXID() ,xid是否传递且一致

    3、主服务加上@GlobalTransactional注解即可,被调用服务不用加@GlobalTransactional和@Transactional

    4、@GlobalTransactional(rollbackFor = Exception.class)最好加上rollbackFor = Exception.class,表示遇到Exception都回滚,不然遇到有些异常(如自定义异常)则不会回滚

    依赖注意如下:

    (1)本身有 spring-cloud-starter-alibaba-nacos-discovery 依赖,不再需要额外添加 nacos-client 依赖

    (2)spring-cloud-starter-alibaba-seata 包含 seata-spring-boot-starter 依赖,但是有些配置有问题,需要自己导入和Seata Server 版本相同的依赖

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-seataartifactId>
    4. <exclusions>
    5. <exclusion>
    6. <groupId>io.seatagroupId>
    7. <artifactId>seata-spring-boot-starterartifactId>
    8. exclusion>
    9. exclusions>
    10. dependency>
    11. <dependency>
    12. <groupId>io.seatagroupId>
    13. <artifactId>seata-spring-boot-starterartifactId>
    14. <version>1.4.2version>
    15. dependency>
    1. <groupId>com.companygroupId>
    2. <artifactId>springcloud-alibaba-2-seata-distributed-orderartifactId>
    3. <version>1.0.0version>
    4. <name>springcloud-alibaba-2-seata-distributed-ordername>
    5. <description>Demo project for Spring Bootdescription>
    6. <properties>
    7. <java.version>1.8java.version>
    8. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    9. <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    10. <spring-boot.version>2.3.12.RELEASEspring-boot.version>
    11. <spring-cloud-alibaba.version>2.2.7.RELEASEspring-cloud-alibaba.version>
    12. properties>
    13. <dependencies>
    14. <dependency>
    15. <groupId>org.springframework.bootgroupId>
    16. <artifactId>spring-boot-starter-webartifactId>
    17. dependency>
    18. <dependency>
    19. <groupId>com.alibaba.cloudgroupId>
    20. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    21. dependency>
    22. <dependency>
    23. <groupId>org.springframework.bootgroupId>
    24. <artifactId>spring-boot-starter-testartifactId>
    25. <scope>testscope>
    26. <exclusions>
    27. <exclusion>
    28. <groupId>org.junit.vintagegroupId>
    29. <artifactId>junit-vintage-engineartifactId>
    30. exclusion>
    31. exclusions>
    32. dependency>
    33. <dependency>
    34. <groupId>org.projectlombokgroupId>
    35. <artifactId>lombokartifactId>
    36. dependency>
    37. <dependency>
    38. <groupId>mysqlgroupId>
    39. <artifactId>mysql-connector-javaartifactId>
    40. dependency>
    41. <dependency>
    42. <groupId>org.mybatis.spring.bootgroupId>
    43. <artifactId>mybatis-spring-boot-starterartifactId>
    44. <version>2.1.3version>
    45. dependency>
    46. <dependency>
    47. <groupId>com.alibaba.cloudgroupId>
    48. <artifactId>spring-cloud-starter-alibaba-seataartifactId>
    49. <exclusions>
    50. <exclusion>
    51. <groupId>io.seatagroupId>
    52. <artifactId>seata-spring-boot-starterartifactId>
    53. exclusion>
    54. exclusions>
    55. dependency>
    56. <dependency>
    57. <groupId>io.seatagroupId>
    58. <artifactId>seata-spring-boot-starterartifactId>
    59. <version>1.4.2version>
    60. dependency>
    61. <dependency>
    62. <groupId>com.companygroupId>
    63. <artifactId>springcloud-alibaba-2-seata-distributed-commonsartifactId>
    64. <version>1.0.0version>
    65. dependency>
    66. dependencies>
    67. <dependencyManagement>
    68. <dependencies>
    69. <dependency>
    70. <groupId>com.alibaba.cloudgroupId>
    71. <artifactId>spring-cloud-alibaba-dependenciesartifactId>
    72. <version>${spring-cloud-alibaba.version}version>
    73. <type>pomtype>
    74. <scope>importscope>
    75. dependency>
    76. <dependency>
    77. <groupId>org.springframework.cloudgroupId>
    78. <artifactId>spring-cloud-dependenciesartifactId>
    79. <version>Hoxton.SR12version>
    80. <type>pomtype>
    81. <scope>importscope>
    82. dependency>
    83. <dependency>
    84. <groupId>org.springframework.bootgroupId>
    85. <artifactId>spring-boot-dependenciesartifactId>
    86. <version>${spring-boot.version}version>
    87. <type>pomtype>
    88. <scope>importscope>
    89. dependency>
    90. dependencies>
    91. dependencyManagement>
    92. <build>
    93. <plugins>
    94. <plugin>
    95. <groupId>org.apache.maven.pluginsgroupId>
    96. <artifactId>maven-compiler-pluginartifactId>
    97. <version>3.8.1version>
    98. <configuration>
    99. <source>1.8source>
    100. <target>1.8target>
    101. <encoding>UTF-8encoding>
    102. configuration>
    103. plugin>
    104. <plugin>
    105. <groupId>org.springframework.bootgroupId>
    106. <artifactId>spring-boot-maven-pluginartifactId>
    107. plugin>
    108. plugins>
    109. <resources>
    110. <resource>
    111. <directory>src/main/javadirectory>
    112. <includes>
    113. <include>**/*.xmlinclude>
    114. includes>
    115. resource>
    116. <resource>
    117. <directory>src/main/resourcesdirectory>
    118. <includes>
    119. <include>**/*.*include>
    120. includes>
    121. resource>
    122. resources>
    123. build>
    1. @Slf4j //lombok
    2. @RestController
    3. public class OrderController {
    4. @Autowired
    5. private OrderService orderService;
    6. @RequestMapping("/order")
    7. public Integer createOrder(@RequestParam("userId") Integer userId,
    8. @RequestParam("productId") Integer productId) throws Exception {
    9. log.info("请求下单, 用户:{}, 商品:{}", userId, productId);
    10. return orderService.createOrder(userId, productId);
    11. }
    12. }
    13. @Slf4j
    14. @Service
    15. public class OrderServiceImpl implements OrderService {
    16. @Autowired
    17. private OrdersMapper ordersMapper;
    18. @Autowired
    19. private FeignAccountService accountService;
    20. @Autowired
    21. private FeignProductService productService;
    22. @Override
    23. //seata全局事务注解;name要唯一
    24. @GlobalTransactional//(name = "seata-order-GlobalTransactional",rollbackFor = Exception.class)
    25. public Integer createOrder(Integer userId, Integer productId) {
    26. Integer amount = 1; // 购买数量暂时设置为 1
    27. log.info("当前 XID: {}", RootContext.getXID());
    28. //1、减库存
    29. Product product = productService.reduceStock(productId, amount);
    30. //2、减余额
    31. accountService.reduceBalance(userId, product.getPrice());
    32. //3、下订单
    33. Orders order = new Orders();
    34. order.setUserId(userId);
    35. order.setProductId(productId);
    36. order.setPayAmount(product.getPrice().multiply(new BigDecimal(amount)));
    37. order.setAddTime(new Date());
    38. ordersMapper.insertSelective(order);
    39. //造成异常,测试是否回滚
    40. int a = 10/0;
    41. log.info("下订单: {}", order.getId());
    42. // 返回订单编号
    43. return order.getId();
    44. }
    45. }
  • 相关阅读:
    海上生明月,天涯共此时 ------#征文|程序员过中秋的一百种方式#
    Hive概念及Hive,MySQL安装、配置
    torch.optim 之 Algorithms (Implementation: for-loop, foreach, fused)
    python基础-1(开发环境安装及配置anaconda 5.3.1+py3.7)
    Elasticsearch实战(七)---BestFields MostFields CrossFields 多字段搜索策略
    Java版企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
    Kafka简单入门01
    CMS之织梦导航二级下拉菜单
    每日优鲜深陷“破产风波”,生鲜电商路在何方?
    微服务之SpringCloud AlibabaNacos服务注册和配置中心
  • 原文地址:https://blog.csdn.net/MinggeQingchun/article/details/126225632