微服务 | Springboot整合Seata+Nacos实现分布式事务

07-09 1523阅读

1、分布式事务概念说明

分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如:用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务,必须要保证不同服务状态结果的一致性。本地事务依赖数据库本身提供的事务特性来实现,因此以下逻辑可以控制本地事务

2、分布式事务产生的情景

1.跨JVM进程产生分布式事务

典型的场景就是微服务架构:微服务之间通过 远程调用完成事务操作。比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减少库存

微服务 | Springboot整合Seata+Nacos实现分布式事务

2.跨数据库实例产生分布式事务

单体系统访问多个数据库实例:当单体系统需要访问多个数据库(实例)时就会产生分布式事务。比如:用户信息和订单信息分别在两个MySQL实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务

微服务 | Springboot整合Seata+Nacos实现分布式事务

3.多服务访问同一个数据库实例

订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是 跨JVM进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务

微服务 | Springboot整合Seata+Nacos实现分布式事务

3、分布式事务问题由来

1、单体项目

一个模块对应一个数据库中的多张表

采用本地**@Transactiona**即可解决

2、分布式项目1

多个模块对应一个数据库中的多张表

采用消息中间件即可解决

3、分布式项目2

多个模块对应多个数据库中的多张表

采用消息中间件和Seata都可,偷懒使用Seata,自己实现可以使用消息中间件

4、Seate

1、简介

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

@GlobalTransactional:一个注解搞定一切

2、Seata的三大角色

在 Seata 的架构中,一共有三个角色:

  • TC (Transaction Coordinator) - 事务协调者

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

  • TM (Transaction Manager) - 事务管理器

    定义全局事务的范围:开始全局事务、提交或回滚全局事务。

  • RM (Resource Manager) - 资源管理器

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

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

    案例场景

    用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

    • 仓储服务:对给定的商品扣除仓储数量。
    • 订单服务:根据采购需求创建订单。
    • 帐户服务:从用户帐户中扣除余额。

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      5、环境搭建

      1、安装(seata 1.4.2)

      官网地址:https://github.com/seata/seata/releases

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      2、修改配置文件
      1、file.conf

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      2、registry.conf

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      准备seataServer.properties配置信息

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      service.vgroupMapping.my_test_tx_group=default #此处必须要和配置文件中一致  配置文件在script\config-center\config.txt中
      store.mode=db
      store.db.datasource=druid
      store.db.dbType=mysql
      store.db.driverClassName=com.mysql.cj.jdbc.Driver
      store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=UTC
      store.db.user=root
      store.db.password=123456
      store.db.minConn=5
      store.db.maxConn=30
      store.db.globalTable=global_table
      store.db.branchTable=branch_table
      store.db.distributedLockTable=distributed_lock
      store.db.queryLimit=100
      store.db.lockTable=lock_table
      store.db.maxWait=5000
      

      在下载Source code(zip)源代码中复制script到bin同目录中

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      在script中找到config.txt(seata-server-1.4.2\seata\seata-server-1.4.2\script\config-center)

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      最终目录效果

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      进入conf双击直接运行或者命令运行需要准备git bash环境

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      命令运行

      // 运行指令 ,通过 Git Bash Here
      sh nacos‐config.sh ‐h localhost ‐p 8848 -u nacos -w nacos
      

      成功结果如下

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      3、进入bin目录双击运行seata-server.bat

      成功如下图

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      seata注册成功如图所示

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      6、案例实现

      分别创建三个项目订单*商品*支付

      项目结构

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      数据库结构

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      准备数据库
      /*
       Navicat Premium Data Transfer
       Source Server         : mysql
       Source Server Type    : MySQL
       Source Server Version : 80036 (8.0.36)
       Source Host           : localhost:3306
       Source Schema         : seata
       Target Server Type    : MySQL
       Target Server Version : 80036 (8.0.36)
       File Encoding         : 65001
       Date: 08/07/2024 14:53:40
      */
      SET NAMES utf8mb4;
      SET FOREIGN_KEY_CHECKS = 0;
      -- ----------------------------
      -- Table structure for branch_table
      -- ----------------------------
      DROP TABLE IF EXISTS `branch_table`;
      CREATE TABLE `branch_table`  (
        `branch_id` bigint NOT NULL,
        `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
        `transaction_id` bigint NULL DEFAULT NULL,
        `resource_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `branch_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `status` tinyint NULL DEFAULT NULL,
        `client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `gmt_create` datetime(6) NULL DEFAULT NULL,
        `gmt_modified` datetime(6) NULL DEFAULT NULL,
        PRIMARY KEY (`branch_id`) USING BTREE,
        INDEX `idx_xid`(`xid` ASC) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
      -- ----------------------------
      -- Records of branch_table
      -- ----------------------------
      -- ----------------------------
      -- Table structure for distributed_lock
      -- ----------------------------
      DROP TABLE IF EXISTS `distributed_lock`;
      CREATE TABLE `distributed_lock`  (
        `lock_key` char(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
        `lock_value` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
        `expire` bigint NULL DEFAULT NULL,
        PRIMARY KEY (`lock_key`) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
      -- ----------------------------
      -- Records of distributed_lock
      -- ----------------------------
      INSERT INTO `distributed_lock` VALUES ('AsyncCommitting', ' ', 0);
      INSERT INTO `distributed_lock` VALUES ('RetryCommitting', ' ', 0);
      INSERT INTO `distributed_lock` VALUES ('RetryRollbacking', ' ', 0);
      INSERT INTO `distributed_lock` VALUES ('TxTimeoutCheck', ' ', 0);
      -- ----------------------------
      -- Table structure for global_table
      -- ----------------------------
      DROP TABLE IF EXISTS `global_table`;
      CREATE TABLE `global_table`  (
        `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
        `transaction_id` bigint NULL DEFAULT NULL,
        `status` tinyint NOT NULL,
        `application_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `transaction_service_group` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `transaction_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `timeout` int NULL DEFAULT NULL,
        `begin_time` bigint NULL DEFAULT NULL,
        `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `gmt_create` datetime NULL DEFAULT NULL,
        `gmt_modified` datetime NULL DEFAULT NULL,
        PRIMARY KEY (`xid`) USING BTREE,
        INDEX `idx_status_gmt_modified`(`status` ASC, `gmt_modified` ASC) USING BTREE,
        INDEX `idx_transaction_id`(`transaction_id` ASC) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
      -- ----------------------------
      -- Records of global_table
      -- ----------------------------
      -- ----------------------------
      -- Table structure for lock_table
      -- ----------------------------
      DROP TABLE IF EXISTS `lock_table`;
      CREATE TABLE `lock_table`  (
        `row_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
        `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `transaction_id` bigint NULL DEFAULT NULL,
        `branch_id` bigint NOT NULL,
        `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `table_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `pk` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
        `status` tinyint NOT NULL DEFAULT 0 COMMENT '0:locked ,1:rollbacking',
        `gmt_create` datetime NULL DEFAULT NULL,
        `gmt_modified` datetime NULL DEFAULT NULL,
        PRIMARY KEY (`row_key`) USING BTREE,
        INDEX `idx_status`(`status` ASC) USING BTREE,
        INDEX `idx_branch_id`(`branch_id` ASC) USING BTREE,
        INDEX `idx_xid`(`xid` ASC) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
      -- ----------------------------
      -- Records of lock_table
      -- ----------------------------
      -- ----------------------------
      -- Table structure for undo_log
      -- ----------------------------
      DROP TABLE IF EXISTS `undo_log`;
      CREATE TABLE `undo_log`  (
        `id` bigint NOT NULL AUTO_INCREMENT,
        `branch_id` bigint NOT NULL,
        `xid` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
        `context` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
        `rollback_info` longblob NOT NULL,
        `log_status` int NOT NULL,
        `log_created` datetime NOT NULL,
        `log_modified` datetime NOT NULL,
        PRIMARY KEY (`id`) USING BTREE,
        UNIQUE INDEX `ux_undo_log`(`xid` ASC, `branch_id` ASC) USING BTREE
      ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
      -- ----------------------------
      -- Records of undo_log
      -- ----------------------------
      SET FOREIGN_KEY_CHECKS = 1;
      
      1、依赖
      1、父依赖
       
              8
              8
              UTF-8
              3.2.0-beta.4
              2.6.11
          
          
              
                  
                  
                      org.springframework.boot
                      spring-boot-starter-web
                      ${spring-boot.version}
                      pom
                      import
                  
                  
                  
                      com.alibaba.cloud
                      spring-cloud-alibaba-dependencies
                      2021.0.4.0
                      pom
                      import
                  
                  
                  
                      com.alibaba.cloud
                      spring-cloud-starter-alibaba-nacos-discovery
                      2021.0.4.0
                      pom
                      import
                  
                  
                  
                      org.apache.dubbo
                      dubbo-bom
                      ${dubbo.version}
                      pom
                      import
                  
                  
                  
                      org.apache.dubbo
                      dubbo-spring-boot-starter
                      ${dubbo.version}
                      pom
                      import
                  
              
          
      
      2、子模块依赖
       
              
              
                  com.ruoyi
                  interface
                  1.0-SNAPSHOT
              
              
              
                  org.apache.dubbo
                  dubbo-spring-boot-starter
              
              
              
                  org.springframework.boot
                  spring-boot-starter-web
              
              
              
                  com.alibaba.cloud
                  spring-cloud-starter-alibaba-nacos-discovery
              
              
              
                  com.alibaba.cloud
                  spring-cloud-starter-alibaba-nacos-config
              
              
              
                  mysql
                  mysql-connector-java
                  8.0.33
              
              
              
              
                  com.baomidou
                  mybatis-plus-boot-starter
                  3.5.1
              
              
              
                  com.ruoyi
                  bean
                  1.0-SNAPSHOT
              
              
              
                  com.alibaba.cloud
                  spring-cloud-starter-alibaba-seata
                  
                      
                      
                          io.seata
                          seata-spring-boot-starter
                      
                  
              
              
                  io.seata
                  seata-spring-boot-starter
                  1.4.2
              
          
      
      2、配置文件
      1、bootstrap.yaml
      spring:
        application:
          name: alibaba-seata-provider #服务名称
        cloud:
          nacos:
            discovery:
              server-addr: localhost:8848 #将nacos作为注册中心
      #      config:
      #        server-addr: localhost:8848 #将nacos作为配置中心
      #        file-extension: yaml #指定yaml格式的配置
      
      2、application.yaml
      #dubbo配置
      dubbo:
        application:
          name: Dubbo-provider-9002  #Dubbo服务名称
          qos-enable: false
        protocol:
          name: dubbo  #协议名称
          port: -1  #端口号,-1表示自动分配
        registry:
          address: nacos://localhost:8848 #注册中心地址
        consumer:
          timeout: 10000 #消费者调用超时时间设置
          retries: 0 #消费者重试次数
      server:
        port: 9002
      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
          username: root
          password: 123456
      mybatis-plus:
        type-aliases-package: com.ruoyi.pojo
        mapper-locations: classpath:/mybatis/mapper/*.xml
      #seata配置
      seata:
        enabled: true
        enable-auto-data-source-proxy: true
        tx-service-group: my_test_tx_group #事务分组需要和服务端配置文件中一致
        registry:
          type: nacos
          nacos:
            application : seata-server
            serverAddr : 127.0.0.1:8848
            group : SEATA_GROUP
            cluster : default
            username : nacos
            password : nacos
        config:
          type: nacos
          nacos:
            server-addr: 127.0.0.1:8848
            group: SEATA_GROUP
            username: nacos
            password: nacos
            data-id: seataServer.properties
      

      每个子模块微服务都要加上配置文件

      3、service
          /**
           * 用户余额接口服务
           */
          @DubboReference()
          private BalanceService balanceService;
          /**
           * 商品接口服务
           */
          @DubboReference()
          private GoodsService goodsServiceImpl;
          @GlobalTransactional
          @Override
          public void inserOrder(Order order) {
              //创建订单信息
              orderMapper.insert(order);
              //扣减用户余额信息   这里我是使用dubbo进行调用的  也可以用OpenFeign或者RestTemplate进行RPC调用
              balanceService.updateBalance(new Balance(1,1,new BigDecimal(100),new BigDecimal(1)));
              //扣减商品库存信息
              goodsServiceImpl.updateGoods(new Goods(1,1));  
          }
      

      此处在商品服务中模拟异常

          @Override
          public int updateGoods(Goods goods) {
              //根据商品id获取商品信息
              Goods goods1 = goodsMapper.selectById(goods.getId());
              int i = 10/0;
              System.err.println(i);
              //更新商品库存信息
              goods1.setGoodsCount(goods1.getGoodsSum() - goods.getGoodsCount());
              int update = goodsMapper.updateById(goods1);
              return update;
          }
      
      4、启动类
      @SpringBootApplication
      @EnableDiscoveryClient //nacos注册中心
      @EnableDubbo //开启dubbo
      @EnableAutoDataSourceProxy //开启seata
      public class ProviderApplication_9003 {
          public static void main(String[] args) {
              System.setProperty("spring.cloud.bootstrap.enabled", "true");
              SpringApplication.run(ProviderApplication_9003.class,args);
          }
      }
      

      注意:每个子模块微服务启动类都要加上@EnableAutoDataSourceProxy

      测试

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      微服务 | Springboot整合Seata+Nacos实现分布式事务

      查看seata日志已经回滚

      项目源码:https://gitee.com/peng-pengjun/spring-cloud-alibaba-seata

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]