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


    1、分布式事务概念说明

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

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

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

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

    img

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

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

    img

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

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

    img

    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个微服务提供支持:

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

    Architecture

    5、环境搭建

    1、安装(seata 1.4.2)

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

    在这里插入图片描述

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

    在这里插入图片描述

    2、registry.conf

    在这里插入图片描述

    在这里插入图片描述

    准备seataServer.properties配置信息

    在这里插入图片描述

    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同目录中

    在这里插入图片描述

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

    在这里插入图片描述

    在这里插入图片描述

    最终目录效果

    在这里插入图片描述

    在这里插入图片描述

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

    在这里插入图片描述

    命令运行

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

    成功结果如下

    在这里插入图片描述

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

    成功如下图

    在这里插入图片描述

    seata注册成功如图所示

    在这里插入图片描述

    6、案例实现

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

    项目结构

    在这里插入图片描述

    数据库结构

    在这里插入图片描述

    准备数据库
    /*
     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、父依赖
     <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <dubbo.version>3.2.0-beta.4dubbo.version>
            <spring-boot.version>2.6.11spring-boot.version>
        properties>
    
    
        <dependencyManagement>
            <dependencies>
                
                <dependency>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-starter-webartifactId>
                    <version>${spring-boot.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
                
                <dependency>
                    <groupId>com.alibaba.cloudgroupId>
                    <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                    <version>2021.0.4.0version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
                
                <dependency>
                    <groupId>com.alibaba.cloudgroupId>
                    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
                    <version>2021.0.4.0version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
                
                <dependency>
                    <groupId>org.apache.dubbogroupId>
                    <artifactId>dubbo-bomartifactId>
                    <version>${dubbo.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
                
                <dependency>
                    <groupId>org.apache.dubbogroupId>
                    <artifactId>dubbo-spring-boot-starterartifactId>
                    <version>${dubbo.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
            dependencies>
        dependencyManagement>
    
    2、子模块依赖
     <dependencies>
            
            <dependency>
                <groupId>com.ruoyigroupId>
                <artifactId>interfaceartifactId>
                <version>1.0-SNAPSHOTversion>
            dependency>
    
            
            <dependency>
                <groupId>org.apache.dubbogroupId>
                <artifactId>dubbo-spring-boot-starterartifactId>
            dependency>
    
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
    
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
    
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>8.0.33version>
            dependency>
    
            
            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>3.5.1version>
            dependency>
    
    
            
            <dependency>
                <groupId>com.ruoyigroupId>
                <artifactId>beanartifactId>
                <version>1.0-SNAPSHOTversion>
            dependency>
    
    
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-seataartifactId>
                <exclusions>
                    
                    <exclusion>
                        <groupId>io.seatagroupId>
                        <artifactId>seata-spring-boot-starterartifactId>
                    exclusion>
                exclusions>
            dependency>
            <dependency>
                <groupId>io.seatagroupId>
                <artifactId>seata-spring-boot-starterartifactId>
                <version>1.4.2version>
            dependency>
    
    
        dependencies>
    
    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

    测试

    在这里插入图片描述

    在这里插入图片描述

    查看seata日志已经回滚

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

  • 相关阅读:
    T31开发笔记:metaipc测试
    高性能对象存储minio
    Leetcode 【1155. 掷骰子等于目标和的方法数】
    抖音本地生活服务商申请怎么做?无保证金的申请方法来了
    取消SIM卡槽,eSIM为防盗带来新转机,手机防盗终极大招
    仿京东放大镜效果(pink老师版)
    ModStart 中HasAdminQuickCRUD 的功能使用解析
    低端电脑如何深度学习秘籍-使用mistGPU计算平台
    物联网(IoT):连接未来的万物之网
    猿创征文|【C++游戏引擎Easy2D】炫酷动画来这学,位移动画构造函数让节点执行动画
  • 原文地址:https://blog.csdn.net/m0_60500421/article/details/140269549