微服务SpringCloud分布式事务之Seata
视频教程:https://www.bilibili.com/video/BV16P63Y3ESq
效果演示
准备的微服务项目调用的链路如下:
文字描述:
- gateway模块接收到请求,并发送到order订单模块
- order订单模块接收到请求,新增一个订单数据后发送一个请求到pay支付模块
- pay支付模块接收到请求,发送一个模块到account账户模块扣减余额并新增一条支付数据
- account账户模块接收到请求,扣减账户余额
当我未标记 @GlobalTransactional
注解的时候,如图:
在这种情况下很理所应当的报错:
但是我成功的创建了支付信息:
且成功的扣减了余额:
只有订单模块的事务时成功的:
当我修改为使用Seata的分布式事务注解 @GlobalTransactional
:
同样发生了错误:
余额没有扣减:
没有新增订单信息:
也没有形成支付信息:
这证明了分布式事务Seata搭建成功,接下来我们一个一个步骤搭建一下这个框架。
项目准备
之前我有写过一篇关于SpringCloud整合Micrometer做链路追踪的文章,这篇文章当中准备了一个项目。可以使用下面两个连接下载整合了Micrometer但未整合Seata的项目代码(任选一个即可):
- zip压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.1.0.zip
- tar.gz压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.1.0.tar.gz
也可以选择未整合Micrometer的源代码(任选一个即可):
- zip压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.0.0.zip
- tar.gz压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.0.0.tar.gz
整合了Seata做分布式事务的代码:
- zip压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.2.0.zip
- tar.gz压缩包:https://github.com/xiaohh-me/xiaohh-cloud-micrometer/archive/refs/tags/v1.2.0.tar.gz
该项目所使用到的技术栈:
技术栈 | 版本 |
---|---|
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。也可以使用下面这行命令下载:
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压缩等软件进行解压:
tar -zxvf apache-seata-2.2.0-incubating-bin.tar.gz
解压之后目录如图:
此次只需要关注到 seata-server
目录的内容即可,目录内容:
需要修改这个目录下 conf/application.yml
文件,文件内容(注意需要修改nacos注册中心和配置中心,还有数据库配置):
#
# 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当中执行下面几行命令(可以改为自己的数据库名字哦):
drop database if exists `xiaohh_seata`;
create database `xiaohh_seata`;
use `xiaohh_seata`;
source seata_server.sql
执行如图:
然后确定你的seata配置文件没问题之后,先启动nacos,再启动seata。在确保nacos正常启动之后执行下面命令启动seata:
- Windows:
bin/seata-server.bat
- Mac/Linux:
./bin/seata-server.sh
Mac启动如图:
启动之后到对应的nacos命名空间下查看,可以看到seata已经以 seata-server
的名字注册到nacos注册中心中:
至此,seata安装和启动成功
修改项目代码
添加项目依赖
如果你的项目和我提供的项目一样,在聚合 pom.xml
或着项目的 pom.xml
当中添加了 spring-cloud-alibaba-dependencies
的依赖管理,如图:
则只需要在参与到分布式事务微服务的 pom.xml
中添加如下依赖即可:
<!-- Seata 分布式事务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
添加如图:
修改bootstrap.yaml配置文件
需要修改参与到分布式事务微服务模块的 bootstrap.yaml 配置文件,告诉微服务模块seata的位置,添加的配置如下:
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 当中,也可以复制下面这行建表语句:
-- 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
语句前加上下面这行代码:
if (true) throw new RuntimeException("测试分布式事务失败异常");
添加如图:
测试分布式事务是否成功
接下来就可以测试分布式事务了,首先发送获取账户余额的请求,可以看到账户余额为10000:
然后请求分布式事务的接口,可以看到报错了:
然后再次请求获取账户余额接口,可以看到余额并没有被扣减:
也没有支付信息的产生:
可以确定分布式事务搭建成功!
修改报错消息
可以看到请求分布式事务接口时候,报错消息并不是代码中写的分布式消息,而是Seata返回的错误消息:
需要修改一下统一异常返回 RestControllerAdvice
,将 e.getMessage()
改为 e.getCause().getMessage()
即可,如图:
然后重启项目再次请求发现改为了代码当中写的Message:
好了,到此为止Seata分布式事务搭建成功