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

avatar
作者
筋斗云
阅读量:0

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>8</maven.compiler.source>         <maven.compiler.target>8</maven.compiler.target>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>         <dubbo.version>3.2.0-beta.4</dubbo.version>         <spring-boot.version>2.6.11</spring-boot.version>     </properties>       <dependencyManagement>         <dependencies>             <!-- Spring Boot -->             <dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-starter-web</artifactId>                 <version>${spring-boot.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>              <!--springcloudalibaba-->             <dependency>                 <groupId>com.alibaba.cloud</groupId>                 <artifactId>spring-cloud-alibaba-dependencies</artifactId>                 <version>2021.0.4.0</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>              <!--nacos-->             <dependency>                 <groupId>com.alibaba.cloud</groupId>                 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>                 <version>2021.0.4.0</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>              <!-- Dubbo -->             <dependency>                 <groupId>org.apache.dubbo</groupId>                 <artifactId>dubbo-bom</artifactId>                 <version>${dubbo.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>              <!-- Dubbo -->             <dependency>                 <groupId>org.apache.dubbo</groupId>                 <artifactId>dubbo-spring-boot-starter</artifactId>                 <version>${dubbo.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>          </dependencies>     </dependencyManagement> 
2、子模块依赖
 <dependencies>         <!-- Api接口 -->         <dependency>             <groupId>com.ruoyi</groupId>             <artifactId>interface</artifactId>             <version>1.0-SNAPSHOT</version>         </dependency>          <!-- dubbo -->         <dependency>             <groupId>org.apache.dubbo</groupId>             <artifactId>dubbo-spring-boot-starter</artifactId>         </dependency>           <!-- spring boot starter -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>           <!--Nacos-->         <dependency>             <groupId>com.alibaba.cloud</groupId>             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>         </dependency>           <!--config-->         <dependency>             <groupId>com.alibaba.cloud</groupId>             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>         </dependency>          <!--mysql-->         <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>             <version>8.0.33</version>         </dependency>          <!--mybatis-plus-->         <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->         <dependency>             <groupId>com.baomidou</groupId>             <artifactId>mybatis-plus-boot-starter</artifactId>             <version>3.5.1</version>         </dependency>           <!--实体类-->         <dependency>             <groupId>com.ruoyi</groupId>             <artifactId>bean</artifactId>             <version>1.0-SNAPSHOT</version>         </dependency>           <!--seata-->         <dependency>             <groupId>com.alibaba.cloud</groupId>             <artifactId>spring-cloud-starter-alibaba-seata</artifactId>             <exclusions>                 <!-- 要与seata服务端版本一直,所以把自带的替换掉 -->                 <exclusion>                     <groupId>io.seata</groupId>                     <artifactId>seata-spring-boot-starter</artifactId>                 </exclusion>             </exclusions>         </dependency>         <dependency>             <groupId>io.seata</groupId>             <artifactId>seata-spring-boot-starter</artifactId>             <version>1.4.2</version>         </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

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!