目录
Seata是什么?
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。本博客使用seata的AT模式,AT的英文:Automatic Transaction,当前最使用最多的模式当然是AT模式。
版本信息
- JDK : 1.8.0_65
- SpringCloud: Hoxton.SR1
- Nacos : 1.1.4
- Nginx : nginx-1.18.0
- Spring-cloud-alibaba : 2.2.0
- Seata : 1.2.0
- linux : Linux version 3.10.0-327.el7.x86_64(Red Hat)
Nacos集群安装
- nacos集群安装的官方说明 ,如果你已经安装好,可以跳过此步骤,nacos默认使用的端口号是8848,假如你想更改默认端口但是又不想通过修改application.propertis中的server.port=8848来实现的话,很遗憾官网没有说明怎么更改,如果你不会怎么更改,那么此步骤可能对你非常有帮助。
- 准备三台服务器,主机名分别记为: spark5,spark6,spark7
- 官网下载安装包: https://github.com/alibaba/nacos/releases/tag/1.1.4 。当然也可以选择其他的版本。下载安装包nacos-server-1.1.4.tar.gz。
- 解压 ,安装包
tar -zxvf nacos-server-1.1.4.tar.gz -C /usr/local/module
解压得到 nacos ,进入到 nacos,nacos下有conf文件夹,conf文件夹下除了nacos的基本配置文件外还有nacos-mysql.sql
cd nacos cd conf
- nacos内置derby数据库,nacos使用数据库进行持久化,这样就保证了当nacos宕机或者重启之后注册到其中的各种配置数据都还在,nacos集群还可以保证高可用。这就是为什么使用nacos集群的理由,nacos集群的每个节点都自带derby数据源,这样就带来了各个nacos节点数据有可能不一直的一致性问题,所以在nacos集群中就不能再使用内置数据源derby,使用mysql当作外置数据源,nacos集群中的各个节点都使用同一个外置数据源,这样就可以规避nacos的数据一致性问题。
准备一个mysql数据源 暂且记作 nacos_config user nacos_config 执行conf 文件夹下面的 nacos-mysql.sql 这样就准备好了mysql数据库了
- 在conf文件下的application.properties中指定使用mysql当前外置数据源以及mysql数据源的详细信息。
vim application.properties
在 文件的末尾添加如果下内容,mysql的信息内容请根据的情况进行更改。
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.5.100:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=python123
- 编辑cluster.conf配置文件,指定nacos的所有节点,各个nacos节点的信息 ip:port ,ip必须是 hostname -i 识别的 ip
vim cluster.conf
nacos的端口我们使用的还是8848,你可以根据你的各个主机的端口使用情况,使用其他的可用端口
添加的内容如下:
192.168.35.135:8848
192.168.35.136:8848
192.168.35.137:8848
- 编辑startup.sh 来指定nacos节点启动之后使用的端口号
在 startup.sh的第57行添加一个选项(这里我们就当作 t )以及在 第65行下面该选项要执行的脚本代码块。
vim startup.sh +57
添加之前的状态:
while getopts ":m:f:s:" opt
do
case $opt in
m)
MODE=$OPTARG;;
f)
FUNCTION_MODE=$OPTARG;;
s)
SERVER=$OPTARG;;
?)
echo "Unknown parameter"
exit 1;;
esac
done
修改之后的状态:
while getopts ":m:f:s:t:" opt
do
case $opt in
m)
MODE=$OPTARG;;
f)
FUNCTION_MODE=$OPTARG;;
s)
SERVER=$OPTARG;;
t)
PORT=$OPTARG;;
?)
echo "Unknown parameter"
exit 1;;
esac
done
修改之后(1) while getopts ":m:f:s:t:" opt 多了一个 t
修改之后(2) 还 多了一个
t)
PORT=$OPTARG;;
还没有完成对startup.sh的编辑,在 startup.sh 的第134行进行更改。
vim startup.sh +134
第134 行 修改之前的状态
nohup $JAVA ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
第134 行 修改之后的状态
nohup $JAVA -Dserver.port=${PORT} ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
就是多了 “-Dserver.port=${PORT}”,其中的PORT 变量对应于 57 行添加的 t 选项设置的变量
- 创建一个nacos启动脚本,指定naocs的端口号,这样我们以后就不会忘记nacos已经规划好要使用的端口号了。题外话:创建一个启动脚本从而对原始启动脚本进行包装的好处:这里启动只是传递端口号一个参数,如果是别的脚本要传很多参数的话,好处更加明显,在生产环境中会给我们开发带来很多的便利。
创建一个nacos的启动脚本,对原始启动 startup.sh 进行包装 ,记作 : my_startup.sh
vim my_startup.sh
编辑内容如下:
sh startup.sh -p 8848
保存退出
给 脚本添加执行权限
chmod u+x my_startup.sh
注意: 端口号不一定是 8848 ,根据的情况自行指定,但是必须跟 cluster.conf 中指定的一样
- nginx的安装省略,我们使用nginx来实现对nacos集群负载均衡,注意了:不是对微服务的负载均衡,微服务的负载均衡是我们是通过nacos来实现的。nginx这里提供一个虚拟ip,并且实现对nacos的负载均衡,nginx默认的负载均衡方式是轮询的方式,nacos各个节点的权重都是 1 。
(1)在 nginx.conf 的 33 行(第33 行的内容是 #gzip on;) 的下面 nacos各个节点的ip和端口号
vim nginx.conf +33
添加的内容是
34 upstream nacos_cluster{
35 server spark5:8848;
36 server spark6:8848;
37 server spark7:8848;
38 }
spark5,spark6,spark7 都是主机名,对应的ip地址分别是
spark5 ----- 192.168.35.135
spark6 ----- 192.168.35.136
spark7 ----- 192.168.35.137
(2)把nginx默认的端口号更改成为我们想要监听的端口80,我们这里使用的是 7011
更改之后如下所示 :
server {
listen 7011;
server_name spark5;
(3)把 location / {}对应的默认映射全部注掉,更改我们的 cluster
更改后:
48 location / {
49 #root html;
50 #index index.html index.htm;
51 proxy_pass http://nacos_cluster;
52 }
- 创建nginx的启动脚本(包装原始启动脚本)
进入nginx的sbin目录:
cd /usr/local/module/nginx/sbin
sbin目录下有 nginx 一个启动脚本,下面我们创建一个启动脚本对这个nginx原始启动脚本进行包装
vim startup.sh
添加的内容如下所示:
./nginx -c /usr/local/module/nginx/conf/nginx.conf
保存退出,这样就完成了nginx的配置了。
给 startup.sh 添加执行权限
chmod u+x startup.sh
- 将配置好的nacos解压包分发给其他两个节点 ,当前结点 是 spark5 ,另外两个节点是 spark6,spark7。同步文件使用 xsync 或者 scp 均可实现,前提 是 这三个服务器节点直接必须配置好ssh免密登陆。
- 启动各个nacos节点,启动nginx,在浏览器输入 spark5:7011/nacos就可以访问登陆nacos了,账号和密码都是nacos,用户和密码都在上面准备好的名为 nacos_config 的MySQL数据库中的user表里面记录。登陆的页面如下:
nacos的home页面如下所示,到此nacos的集群安装完毕。
Seata的集群安装
- SeaTa集群的安装官网的博客有:http://seata.io/zh-cn/blog/seata-quick-start.html#,如果你参照官网的博客就可以跳过此节,虽然这里跟官网有一些不同,不过大多数都是相同的,我也是参照官网的这个博客进行seata集群安装的。
- seata有三个组件,分别是 TC,TM,RM
TC (Transaction Coordinator) : 事物协调器,维护全局事物的运行状态,协调全局事物的提交与回滚
TM (Transaction Manager) : 控制全局事物的边界,负责开启一个全局事物,并且最终发起全局事物的提交或者回滚。
RM (Resource Manager) : 控制分支事物,负责分支事物的注册、状态汇报,接受事物协调器的指令,启动本地事物的提交或者回滚。
太抽象了。落地实现就是:
(1)seata 服务器(seata集群)相当于 TC
(2) 分布式事物我们使用一个注解,@GlobalTransactional,那么标注这个注解的地方就是 TM ,因为有这个使用这个注解的话就是就是开启一个全局注解,从这里开启全局注解。
(3) 在方法 A 有 @GlobalTransactional ,那么方法 A 就是 TM,如果方法A 调用 其他业务模块的服务(有数据库操作),加入调用的其他的服务 B 、C,那么 B、 C 就是 RM
- 安装包下载:https://github.com/seata/seata/releases/tag/v1.2.0,下载安装包seata-server-1.2.0.tar.gz 与源码包Source code(zip)
(1) 解压安装包 tar -zxvf seata-server-1.1.0.tar.gz -C /usr/local/module (2) 解压得到 seata 包 ,进入 seata, cd seata ll ./ seata中的文件如下所示 ===================================================================== drwxrwxr-x. 2 hadoop hadoop 96 Jun 22 10:13 bin drwxr-xr-x. 3 hadoop hadoop 4096 Jun 23 00:42 conf drwxr-xr-x. 3 hadoop hadoop 8192 Apr 21 14:11 lib -rw-r--r--. 1 hadoop hadoop 11365 May 13 2019 LICENSE ====================================================================
- 为seata准备一个MySQL数据库,暂且记为 seata,初始化这个MySQL数据库
(1) 解压 seata源码包 Source code(zip),得到 seata-1.2.0 ll seata-1.2.0/script/server/db ========================================================= -rw-rw-r--. 1 hadoop hadoop 1975 Apr 21 12:40 mysql.sql -rw-rw-r--. 1 hadoop hadoop 1864 Apr 21 12:40 oracle.sql -rw-rw-r--. 1 hadoop hadoop 1992 Apr 21 12:40 postgresql.sql [hadoop@dsjrz5 db]$ pwd /opt/software/seata-1.2.0/script/server/db ========================================================= 我们登录刚刚准备的seata数据,执行 mysql.sql 脚本,这样我们就完成对mysql数据库的初始化
完成对mysql数据库的,效果如下:
-
修改
conf/file
配置文件,修改使用 db 数据库,实现 Seata TC Server 的全局事务会话信息的共享,如下图所示 -
设置使用 Nacos 注册中心,修改
conf/registry.conf
配置文件,设置使用 Nacos 注册中心。如下图所示: - seata的启动是bin/seata-server.sh,现在我们要创建对原始的启动脚本进行包装,创建新的启动脚本
(1 ) 进入到
cd /usr/local/module/seata/bin
vim server-startup.sh
在 新创建的server-startup.sh 中添加的内容如下所示:
nohup sh seata-server.sh -p 18091 -n 1 > seata.log 2>&1 &
(2) 几个参数解释:
-p:Seata TC Server 监听的端口。
-n:Server node。在多个 TC Server 时,需区分各自节点,用于生成不同区间的 transactionId 事务编号,以免冲突。
我的seata节点只有两个,spark5 ,spark7,当前节点是spark5,
在另外一个节点创建的启动脚本的参数除了 n 之外都是相同的。当前节点给 1 ,那么在 spark7中 的 n 就设置为 2 即可
> seata.log 2>&1 : 将 seata的info与error日志都输出到 seata.log 中
(3) 最后保存退出,记得赋予文件执行的权限!!!!!
chmod u+x server-startup.sh
- 启动两个seata服务节点,打开nacos控制台,seata集群的两个节点都可以看到,在如下所示
-
至此,环境都已经搭建好了,接下来就可以愉快的编码了。
代码开发及使用seata进行分布式事务测试
- 使用 的jar包依赖有nacos,druid,seata,mysql-connector,lombok,mybatis,openfeign(接口的方式访问远程服务),业务需求参照官网例子如下:
- 我搭建3 个微服务 ,分别为
seata-order-service,seata-store-service,seata-account-service
- 为三个微服务准备三个数据库,分别为 seata_order,seata_store,seata_account。三个数据库都必须各自创建
因为我们是使用的AT 模式,所以必须创建。undo_log的sql语句官网以及提供。
(1) 进入seata解压源码seata-1.2.0/script/client/at/db 文件夹内
cd seata-1.2.0/script/client/at/db
ll ./
===================================================
-rw-rw-r--. 1 hadoop hadoop 1975 Apr 21 12:40 mysql.sql
-rw-rw-r--. 1 hadoop hadoop 1864 Apr 21 12:40 oracle.sql
-rw-rw-r--. 1 hadoop hadoop 1992 Apr 21 12:40 postgresql.sql
===================================================
三个sql文件对应的表数据结构是一样的。
(2)在三个seata_order,seata_store,seata_account数据库中都必须创建。
mysql.sql脚本文件中的undo_log表的内容如下:
-- 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`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) 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 NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
- 除了undo_log表之外,三个数据库都必须先创建各自的业务表,这里就省略了。
- pom.xml配置都是相同的。如下所示
<dependencies>
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<!--<version>2.2.0.RELEASE</version>-->
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
<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.2.0</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--web-actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--mysql-druid-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
相关组件的版本说明 (只是版本说明,依赖在上一个图都已经全部给出了)
几个如果如果没有配置好就容易出兼容的版本说明(只列几个比较重要的,其他的就不列了):
(1)spring-cloud:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
(2)spring-boot:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
(3)spring-alibaba:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
(4) druid:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>
(5) mysql-connector :
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- yml文件配置,这里我们的主题是seata,所以我们就着重讲解seata的yml配置,seata1.2.0的微服务配置已经不需要像seata1.0以下版本一样复制seata-server安装时候的file.conf与registry.conf 到项目的resources文件夹下了,而是使用nacos配置file.conf,配置好之后微服务在项目启动的时候就直接到nacos中读取即可,而 registry.conf 的配置信息直接在yml进行配置即可。既然这样,那么我们需要将file.conf中的配置信息配置到nacos配置中心。file.conf中的配置信息不少,手工创建费事还容易手误,官方给我们提供方法。说明:我们的微服务对于seata-server集群来说就是seata客户端。
- 将 file.conf配置信息注册到nacos中心供微服务读取使用。
下载的seata1.2.0源码包解压之后找含有file.conf文件的配置信息,file.conf的配置信息都在 config.txt 文件中 (1) 进入到config.txt文件所在的文件夹 cd seata-1.2.0/script/config-center ll ./ 如下所示我们 可以看到 config.txt ===================================================== hadoop@dsjrz5 config-center]$ ll total 8 drwxrwxr-x. 2 hadoop hadoop 29 Apr 21 12:40 apollo -rw-rw-r--. 1 hadoop hadoop 2473 Apr 21 12:40 config.txt drwxrwxr-x. 2 hadoop hadoop 29 Apr 21 12:40 consul drwxrwxr-x. 2 hadoop hadoop 28 Apr 21 12:40 etcd3 drwxrwxr-x. 2 hadoop hadoop 50 Apr 21 12:40 nacos -rw-rw-r--. 1 hadoop hadoop 3837 Apr 21 12:40 README.md drwxrwxr-x. 2 hadoop hadoop 25 Apr 21 12:40 zk [hadoop@dsjrz5 config-center]$ pwd /opt/software/seata-1.2.0/script/config-center [hadoop@dsjrz5 config-center]$ ========================================================================== (2) 因为我们使用的注册中心是nacos,所以我们进入到nacos文件夹中注册config.txt文件中的信息到nacos注册中心的脚本 cd nacos ll ./ 如下所示,我们看到 ========================================================================== [hadoop@dsjrz5 nacos]$ ll total 8 -rw-rw-r--. 1 hadoop hadoop 1118 Apr 21 12:40 nacos-config.py -rw-rw-r--. 1 hadoop hadoop 2253 Apr 21 12:40 nacos-config.sh [hadoop@dsjrz5 nacos]$ pwd /opt/software/seata-1.2.0/script/config-center/nacos [hadoop@dsjrz5 nacos]$ ========================================================================== (3) 以上两步只是让大家知道脚本以及配置文件在哪里而已,在真正执行将config.txt中的配置注册到nacos注册中心之前 必须先对config.txt做修改,我们要根据根据我们安装 seata-server的时候的配置做相应的相应的修改,主要有下面几个要点: store.db 中的几个修改,数据库的信息,数据源指定为druid,数据库的连接url,用户名等 store.mode=file 更改为 store.mode=db store.db.maxWait=5000 更改为 store.db.maxWait=50000 service.default.grouplist=127.0.0.1:8091 中的值更改为 两个seata-server的主机及其监听端口 service.default.grouplist=192.168.35.135:18091,192.168.35.137:18091 其他保持默认即可。 (4) 更改好 config.txt文件之后重新进入到 /opt/software/seata-1.2.0/script/config-center/nacos中执行脚本。 sh nacos-config.sh 192.168.35.135:7011 执行这个脚本应该注意的几个点: 第一个注意点:config.txt 是在 nacos-config.sh的上一级文件中,而不是在同一级文件夹中, 如果执行的过程中碰到找不到 config.txt 文件,那么除了通过将config.txt文件放到nacos-config.sh的上一级目录中, 还可以直接修改 nacos-config.sh中找 目标文件config.txt的路径。 第二个注意点:192.168.35.135:7011 代表的是 nginx对nacos集群的虚拟ip 执行完毕之后如果没有报错,同时提示可以启动seata-server了,那么就是代表config.txt文件的配置信息已经注册到注册中心。
如果我们能够注册config.txt文件中的配置信息到nacos注册中心,那么我们在nacos注册中心是可以看到的。如下图所示
- 配置yml文件进行配置,我们对seata进行配置,我们从解压的源码seata1.2.0找,因为怎么进行seata的spring配置官方都已经给出了,我可以直接复制粘贴然后修改即可。我们进入到 seata-1.2.0/script/client/spring 文件夹里面,我们看见 application.yml与application.xml两个文件,这两个的配置都是一样的,我们就直接将 application.yml里面的内容复制到我们微服务的application.yml;我们根据我们的情况进行修改以及删除一些有默认值的配置和没有使用到的属性即可。我配置好之后的application.yml如下所示
# seata配置 seata: enabled: true # 开启自动配置 #将 RM 管理的本地事物注册到seata-server,application-id的名称跟微服务名相同即可,启动日志可验证 application-id: ${spring.application.name} tx-service-group: my_test_tx_group service: #seata-server对于微服务来说是server,但是seata-server对于nacos来说就是client客户端 vgroup-mapping: my_test_tx_group: default grouplist: # default: 192.168.35.135:18091,192.168.35.137:18091 config: #这里说明的是config.txt(也就是file.conf)中的配置信息到底在nacos注册中心nacos的哪里 type: nacos nacos: namespace: "" serverAddr: 192.168.35.135:7011 group: SEATA_GROUP userName: "nacos" password: "nacos" registry: # 这里配置说明的是seata-server注册到nacos的哪个地方 type: nacos nacos: application: seata-server server-addr: 192.168.35.135:7011 namespace: "e9f23f54-2444-4ebc-80e4-4072459ebc5d" userName: "nacos" password: "nacos" # 微服务的运行端口 server: port: 7001 # spring配置 spring: application: name: seata-order-service datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.5.100:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true&rewriteBatchedStatements=true username: root password: python123 # nacos配置,将微服务注册到nacos中让需要调用的客户端发现并且调用。 cloud: nacos: discovery: namespace: "" server-addr: 192.168.35.135:7011 #ip微服务运行的主机ip地址,如果没有配置,那么nacos就认为是微服务运行的ip是nacos的网关 ip: 192.168.5.100 service: ${spring.application.name} alibaba: seata: tx-service-group: my_test_tx_group logging: level: io: seata: info #mybatis的配置文件 mybatis: mapper-locations: classpath:mapper/*.xml feign: httpclient: connection-timeout: 60000
另外两个seata-store-service,seata-account-service微服务的yml配置基本相同,除了端口号与 微服务名不一样之外。注意一点:yml配置文件中微服务运行的servlet服务器的ip地址一定要配置,如果没有配置的话,那么nacos认为注册的微服务的ip是nacos的网关,如果别的服务调用的话就报错。例如 微服务在tomcat的ip地址是A,nacos地址是 B,nacos的网关是 C,如果没有配置微服务的ip地址的话,那么nacos认为注册进来的微服务运行在 C上,如果别的服务发现并调用,肯定会报错。
- 启动类,其他seata-store-service,seata-account-service两个微服务主启动类照葫芦画瓢。
- 3个微服务的controller分别如下所示
微服务order:
===================================================
@RestController
@Slf4j
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/order/create")
public CommonResult create(Order order) {
System.out.println("controller端获得的order:" + order);
orderService.create(order);
return new CommonResult(200, "订单创建成功");
}
}
===================================================
微服务store:
=========================================================
@RestController
@Slf4j
public class StorageController {
@Autowired
private StorageService storageService;
//扣减库存
@RequestMapping("/storage/decrease")
public CommonResult decrease(Long productId, Integer count) {
storageService.decrease(productId, count);
return new CommonResult(200, "扣减库存成功!");
}
}
=========================================================
微服务account:
=========================================================
@RestController
@Slf4j
public class AccountController {
@Resource
AccountService accountService;
/**
* 扣减账户余额
*/
@RequestMapping("/account/decrease")
public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) {
accountService.decrease(userId, money);
return new CommonResult(200, "扣减账户余额成功!");
}
}
=========================================================
- 在 微服务seata-order-service中通过openFeign调用seata-store-service与seata-account-service的服务。方法如下
在seata-order-service模块中为store与account分别创建两个service接口即可。
注意事项:@FeignClient(value = "seata-store-service")中的value的值必须是被调用服务的spring-application-name。
调用 seata-store-service服务的service:
==============================================================================
@Component
@FeignClient(value = "seata-store-service")
public interface StorageService {
@PostMapping(value = "/storage/decrease")
CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
==============================================================================
调用 seata-account-service服务的service:
==============================================================================
@Component
@FeignClient(value = "seata-account-service")
public interface AccountService {
@PostMapping(value = "/account/decrease")
CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
==============================================================================
- seata-order-service业务模块中的业务模块,在create方法上添加@GlobalTransactional(name = "seata-order-create",rollbackFor = Exception.class,timeoutMills = 6000000),那么这个方法就是 TM ,在分布式事物中扮演的角色是发起一个全局分布式事务,同时发起全局事务提交或者回滚的决议。----------发起人!!!!--------,不禁让人马老师的云火锅之约的发起者王琦,马老师就相当于分布式事务中的TC ,协调全局分布式事务,马老师必须协调各方资源,云火锅之约的参与者就是RM 角色,因为各个云火锅之约的参与者必须先向马老师报名(注册),参与者在参加芝麻信用上报名获得参与云火锅之约的统一入场券(相当于事务的Transaction XID),由马云老师统一协调,如果在云火锅之约的过程中,某个环节出现问题并不一定就会事务回滚,比如某个参与者突然家里有事了必须回家一下,像这些小毛病就不要耽误大局了,这些问题的出现在聚会发起指出已经定义好。因为@GlobalTransactional的rollbackFor不一定非得是Exception.class,还可以是更小更具体的Exception的子类,可以自定义,可大可小。
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Resource
private OrderDao orderDao;
@Resource
private StorageService storageService;
@Resource
private AccountService accountService;
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
* @param order
*/
@Override
@GlobalTransactional(name = "seata-order-create",rollbackFor = Exception.class,timeoutMills = 6000000)
public void create(Order order) {
log.info("----->开始新建订单");
System.out.println("传入的order:" + order);
//新建订单
orderDao.create(order);
//扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(), order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(), order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//修改订单状态,从零到1代表已经完成
log.info("----->修改订单状态开始");
orderDao.update(order.getUserId(), 0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了");
}
}
- 启动nacos,seata,nginx,启动 seata-order-service,seata-store-service,seata-account-service,启动成功的idea中的console
- 登录nacos注册中心我们可以看见 我们的3个微服务
- 使用postman测试,如下所示
由于本人水平有限,且是第一次写博客,欢迎留言指正交流,如果你觉得挺好且对你有所帮助,那就给个赞吧。
本文原创,如需转载,请注明出处,谢谢!