微服务SpringCloud分布式事务之Seata

视频教程:https://www.bilibili.com/video/BV16P63Y3ESq

效果演示

准备的微服务项目调用的链路如下:

文字描述:

  • gateway模块接收到请求,并发送到order订单模块
  • order订单模块接收到请求,新增一个订单数据后发送一个请求到pay支付模块
  • pay支付模块接收到请求,发送一个模块到account账户模块扣减余额并新增一条支付数据
  • account账户模块接收到请求,扣减账户余额

当我未标记 @GlobalTransactional 注解的时候,如图:

在这种情况下很理所应当的报错:

但是我成功的创建了支付信息:

且成功的扣减了余额:

只有订单模块的事务时成功的:

当我修改为使用Seata的分布式事务注解 @GlobalTransactional

同样发生了错误:

余额没有扣减:

没有新增订单信息:

也没有形成支付信息:

这证明了分布式事务Seata搭建成功,接下来我们一个一个步骤搭建一下这个框架。

项目准备

之前我有写过一篇关于SpringCloud整合Micrometer做链路追踪的文章,这篇文章当中准备了一个项目。可以使用下面两个连接下载整合了Micrometer但未整合Seata的项目代码(任选一个即可):

也可以选择未整合Micrometer的源代码(任选一个即可):

整合了Seata做分布式事务的代码:

该项目所使用到的技术栈:

技术栈 版本
SpringBoot 3.2.12
SpringCloud 2023.0.4
SpringCloudAlibaba 2023.0.1.0
MyBatisStarter 3.0.4

因为使用到了3.*版本的SpringBoot,所以你需要安装Java17或更高版本。

搭建Seata分布式事务

Seata的安装和运行

本次安装的Seata版本为 2.2.0 ,下载链接为:https://dist.apache.org/repos/dist/release/incubator/seata/2.2.0/apache-seata-2.2.0-incubating-bin.tar.gz。也可以使用下面这行命令下载:

bash 复制代码
curl -LO https://dist.apache.org/repos/dist/release/incubator/seata/2.2.0/apache-seata-2.2.0-incubating-bin.tar.gz

然后可以使用下面命令解压,当然在Windows下你也可以使用如360压缩等软件进行解压:

bash 复制代码
tar -zxvf apache-seata-2.2.0-incubating-bin.tar.gz

解压之后目录如图:

此次只需要关注到 seata-server 目录的内容即可,目录内容:

需要修改这个目录下 conf/application.yml 文件,文件内容(注意需要修改nacos注册中心和配置中心,还有数据库配置):

yaml 复制代码
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${log.home:${user.home}/logs/seata}
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

console:
  user:
    # 控制台的用户名和密码
    username: seata
    password: seata
seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      # nacos 配置中心配置
      server-addr: 127.0.0.1:8848
      namespace: xiaohh-cloud-dev
      group: SEATA_GROUP
      data-id: seataServer.properties
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      # nacos 注册中心配置
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: xiaohh-cloud-dev
      cluster: default
  store:
    # support: file 、 db 、 redis 、 raft
    mode: db
    db:
      # 数据库配置
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.jc.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/xiaohh_seata?rewriteBatchedStatements=true
      user: root
      password: xiaohh
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      vgroup-table: vgroup_table
      query-limit: 1000
      max-wait: 5000
  #  server:
  #    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    csrf-ignore-urls: /metadata/v1/**
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/health,/error,/vgroup/v1/**

数据库建表语句可以通过这个地址获得:https://raw.githubusercontent.com/apache/incubator-seata/refs/heads/master/script/server/db/mysql.sql,也可以通过下面命令在已搭建好Seata分布式事务的代码仓库当中也有:

创建数据库,并执行这个脚本文件,现将命令行的目录移动到项目的sql当中,然后登录mysql并在mysql当中执行下面几行命令(可以改为自己的数据库名字哦):

sql 复制代码
drop database if exists `xiaohh_seata`;
create database `xiaohh_seata`;
use `xiaohh_seata`;
source seata_server.sql

执行如图:

然后确定你的seata配置文件没问题之后,先启动nacos,再启动seata。在确保nacos正常启动之后执行下面命令启动seata:

  • Windows:
bat 复制代码
bin/seata-server.bat
  • Mac/Linux:
bash 复制代码
./bin/seata-server.sh

Mac启动如图:

启动之后到对应的nacos命名空间下查看,可以看到seata已经以 seata-server 的名字注册到nacos注册中心中:

至此,seata安装和启动成功

修改项目代码

添加项目依赖

如果你的项目和我提供的项目一样,在聚合 pom.xml 或着项目的 pom.xml 当中添加了 spring-cloud-alibaba-dependencies 的依赖管理,如图:

则只需要在参与到分布式事务微服务的 pom.xml 中添加如下依赖即可:

xml 复制代码
<!-- Seata 分布式事务 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

添加如图:

修改bootstrap.yaml配置文件

需要修改参与到分布式事务微服务模块的 bootstrap.yaml 配置文件,告诉微服务模块seata的位置,添加的配置如下:

yaml 复制代码
seata:
  # 数据源代理模式
  data-source-proxy-mode: AT
  registry:
    nacos:
      # seata注册到nacos的配置
      server-addr: 127.0.0.1:8848
      namespace: xiaohh-cloud-dev
      group: SEATA_GROUP
      application: seata-server
    type: nacos
  # 采用默认的事务分组
  service:
    vgroup-mapping:
      default_tx_group: default
  tx-service-group: default_tx_group

添加如图:

为业务数据库新建表

AT模式分布式事务需要微服务模块的数据库当中有 undo_log 表,这个表存储了如果分布式事务失败了,应该如何回滚数据。这个表的建表语句在 https://raw.githubusercontent.com/apache/incubator-seata/refs/heads/master/script/client/at/db/mysql.sql 当中,也可以复制下面这行建表语句:

sql 复制代码
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);

所有涉及到分布式事务的数据库都需要这张表:

修改事务注解

接下来来到需要分布式事务的 service 层方法,作者提供的项目该方法为 work.xiaohh.order.service.impl.OrderInfoServiceImpl#insertOrderInfo ,复制前面这段,来到 IDEA 当中双击 Shift 键,在搜索框当中输入即可查询到:

本方法还是使用Spring的事务注解 @Transactional,无法解决分布式事务问题:

需要修改为 @GlobalTransactional

然后在此方法的 return 语句前加上下面这行代码:

java 复制代码
if (true) throw new RuntimeException("测试分布式事务失败异常");

添加如图:

测试分布式事务是否成功

接下来就可以测试分布式事务了,首先发送获取账户余额的请求,可以看到账户余额为10000:

然后请求分布式事务的接口,可以看到报错了:

然后再次请求获取账户余额接口,可以看到余额并没有被扣减:

也没有支付信息的产生:

可以确定分布式事务搭建成功!

修改报错消息

可以看到请求分布式事务接口时候,报错消息并不是代码中写的分布式消息,而是Seata返回的错误消息:

需要修改一下统一异常返回 RestControllerAdvice ,将 e.getMessage() 改为 e.getCause().getMessage() 即可,如图:

然后重启项目再次请求发现改为了代码当中写的Message:

好了,到此为止Seata分布式事务搭建成功

相关推荐
1.14(java)18 小时前
Spring-boot快速上手
java·开发语言·javaee
Darkdreams19 小时前
SpringBoot项目集成ONLYOFFICE
java·spring boot·后端
bropro19 小时前
【Spring Boot】Spring AOP中的环绕通知
spring boot·后端·spring
lhbian19 小时前
【Spring Cloud Alibaba】基于Spring Boot 3.x 搭建教程
java·spring boot·后端
luom010219 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
代码雕刻家20 小时前
3.6.Maven-依赖管理-依赖范围
java·maven
范什么特西20 小时前
狂神报错页面设置
java·tomcat
架构师沉默20 小时前
AI 真的会取代程序员吗?
java·后端·架构
Memory_荒年21 小时前
ReentrantLock 线程安全揭秘:从“锁”到“重入”的魔法
java·后端·源码
L0CK21 小时前
秒杀异步下单业务逻辑梳理
java