Springcloud基础知识(17)- Spring Cloud Alibaba Seata (三) | 配置 db 存储模式、整合 Nacos
本文使用 “Springcloud基础知识(15)- Spring Cloud Alibaba Seata (一) | Seata 简介、事务模式、Seata Server” 里的 Seata Server 1.4.2,演示整合 Nacos 后如何使用事务分组。
1. 配置 db 存储模式
在 Seata Server 的 conf/file.conf 文件里,可以看到 store.mode 的三种存储模式:
模式 | 描述 |
file | 文件存储模式,默认存储模式;该模式为单机模式,全局事务的会话信息在内存中读写,并持久化本地文件 root.data,性能较高。 |
db | 数据库存储模式;该模式为高可用模式,全局事务会话信息通过数据库共享,性能较低。建数据库表 |
redis | 缓存模式;Seata Server 1.3 及以上版本支持该模式,性能较高,但存在事务信息丢失风险, 配置 redis 持久化配置 |
1) 创建数据库并初始化表
在 MariaDB (MySQL) 中,创建一个名为 seata 的数据库实例,并在该数据库内执行以下 SQL。
1 -- the table to store GlobalSession data 2 CREATE TABLE IF NOT EXISTS `global_table` 3 ( 4 `xid` VARCHAR(128) NOT NULL, 5 `transaction_id` BIGINT, 6 `status` TINYINT NOT NULL, 7 `application_id` VARCHAR(32), 8 `transaction_service_group` VARCHAR(32), 9 `transaction_name` VARCHAR(128), 10 `timeout` INT, 11 `begin_time` BIGINT, 12 `application_data` VARCHAR(2000), 13 `gmt_create` DATETIME, 14 `gmt_modified` DATETIME, 15 PRIMARY KEY (`xid`), 16 KEY `idx_status_gmt_modified` (`status` , `gmt_modified`), 17 KEY `idx_transaction_id` (`transaction_id`) 18 ) ENGINE = InnoDB DEFAULT CHARSET = utf8; 19 20 -- the table to store BranchSession data 21 CREATE TABLE IF NOT EXISTS `branch_table` 22 ( 23 `branch_id` BIGINT NOT NULL, 24 `xid` VARCHAR(128) NOT NULL, 25 `transaction_id` BIGINT, 26 `resource_group_id` VARCHAR(32), 27 `resource_id` VARCHAR(256), 28 `branch_type` VARCHAR(8), 29 `status` TINYINT, 30 `client_id` VARCHAR(64), 31 `application_data` VARCHAR(2000), 32 `gmt_create` DATETIME(6), 33 `gmt_modified` DATETIME(6), 34 PRIMARY KEY (`branch_id`), 35 KEY `idx_xid` (`xid`) 36 ) ENGINE = InnoDB DEFAULT CHARSET = utf8; 37 38 -- the table to store lock data 39 CREATE TABLE IF NOT EXISTS `lock_table` 40 ( 41 `row_key` VARCHAR(128) NOT NULL, 42 `xid` VARCHAR(128), 43 `transaction_id` BIGINT, 44 `branch_id` BIGINT NOT NULL, 45 `resource_id` VARCHAR(256), 46 `table_name` VARCHAR(32), 47 `pk` VARCHAR(36), 48 `status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking', 49 `gmt_create` DATETIME, 50 `gmt_modified` DATETIME, 51 PRIMARY KEY (`row_key`), 52 KEY `idx_status` (`status`), 53 KEY `idx_branch_id` (`branch_id`), 54 KEY `idx_xid` (`xid`) 55 ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
2) 修改 conf/file.conf 文件
1 ## transaction log store, only used in seata-server 2 store { 3 ## store mode: file、db、redis 4 mode = "db" 5 6 ... 7 8 ## database store property 9 db { 10 ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. 11 datasource = "druid" 12 ## mysql/oracle/postgresql/h2/oceanbase etc. 13 dbType = "mysql" 14 driverClassName = "com.mysql.jdbc.Driver" 15 ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param 16 url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true" 17 user = "root" 18 password = "123456" 19 minConn = 5 20 maxConn = 100 21 globalTable = "global_table" 22 branchTable = "branch_table" 23 lockTable = "lock_table" 24 queryLimit = 100 25 maxWait = 5000 26 } 27 28 ... 29 }
2. 整合 Nacos
本文使用 “Springcloud基础知识(11)- Spring Cloud Alibaba Nacos (一) | Nacos 简介、服务注册中心” 里的 Nacos 2.1.0 作为 Seata 的注册和配置中心。
1) 修改 conf/registry.conf 文件
1 registry { 2 # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa 3 type = "nacos" 4 5 nacos { 6 application = "seata-server" 7 serverAddr = "127.0.0.1:8848" 8 group = "SEATA_GROUP" 9 namespace = "" 10 cluster = "default" 11 username = "nacos" 12 password = "nacos" 13 } 14 15 ... 16 } 17 18 config { 19 # file、nacos 、apollo、zk、consul、etcd3 20 type = "nacos" 21 22 nacos { 23 serverAddr = "127.0.0.1:8848" 24 namespace = "" 25 group = "SEATA_GROUP" 26 username = "nacos" 27 password = "nacos" 28 dataId = "seataServer.properties" 29 } 30 31 ... 32 33 }
注:设置 type = "nacos" 后,Seata Server 就无法读取 conf/file.conf 里的 db 配置,此时需要把 db 配置部署到 Nacos 配置中心。
2) 在 Nacos 上创建配置
(1) 访问 Nacos 页面创建单个 dataId 配置
从 Seata Server 1.4.2 版本开始,已支持从一个 Nacos dataId 中获取所有配置信息,只需在 Nacos 配置中心添加一个配置项。
浏览器访问 http://localhost:8848/nacos/, 输入登录名和密码(默认 nacos/nacos),点击提交按钮,跳转到 Nacos Server 控制台页面。
在 Nacos Server 控制台的 “配置管理” 下的 “配置列表” 中,点击 “+” 按钮,新建如下配置。
1 Data ID: seataServer.properties 2 Group: SEATA_GROUP 3 配置格式: Properties 4 配置内容: 5 6 # 将 Seata Server 的存储模式修改为 db 7 store.mode=db 8 store.lock.mode=db 9 store.session.mode=db 10 store.db.datasource=druid 11 store.db.dbType=mysql 12 store.db.driverClassName=com.mysql.cj.jdbc.Driver 13 store.db.url=jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true14 store.db.user=nacos 15 store.db.password=nacos 16 store.db.minConn=5 17 store.db.maxConn=30 18 store.db.globalTable=global_table 19 store.db.branchTable=branch_table 20 store.db.lockTable=lock_table 21 store.db.queryLimit=100 22 store.db.maxWait=5000
注:配置内容参考 https://github.com/seata/seata/tree/develop/script/config-center/config.txt,根据项目需要添加到 “配置内容” 栏。
(2) 使用脚本上传多个 dataId 配置
可以下载 https://github.com/seata/seata/tree/develop/script/config-center 目录的文件到本地,该目录包含了 nacos 脚本和 config.txt。
本文直接下载 https://github.com/seata/seata/archive/refs/tags/v1.4.2.zip,解压后把目录中的整个 script 目录复制到本地 Seata Server 的安装目录。
Nacos 脚本 Shell 命令:
$ cd ${SEATAPATH}/script/config-center/nacos/
$ sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t '' -u nacos -w nacos
参数说明:
-h: Nacos 主机,默认 localhost。
-p: Nacos 端口,默认 8848。
-g: Nacos 配置组,默认 'SEATA_GROUP'。
-t: 租户信息,等同于 Nacos 命名空间 ID,默认 '' (public 空间)。
-u: Nacos 用户名,默认 ''。
-w: Nacos 密码,默认 ''。
以上命令会自动读取 config-center/config.txt 文件里的配置,每一行配置在 nacos 配置中心产生一个 dataId。建议在原目录下,新建一个config.txt,根据项目需要添加配置项。
注:在 Seata Server 1.4.2 及以上版本,以上两种配置方式都可以使用。如果使用多个 dataId 配置时,要把 conf/registry.conf 里的 config.nacos.dataId 项注释掉。
3) 重启 Seata Server
C:\seata-server-1.4.2\bin>seata-server -p 8092
1 ... 2 3 SLF4J: A number (18) of logging calls during the initialization phase have been intercepted and are 4 SLF4J: now being replayed. These are subject to the filtering rules of the underlying logging system. 5 SLF4J: See also http://www.slf4j.org/codes.html#replay 6 17:30:52.993 INFO --- [ main] io.seata.config.FileConfiguration : The file name of the operation is registry 7 17:30:52.997 INFO --- [ main] io.seata.config.FileConfiguration : The configuration file used is C:\Applications\Java\alibaba-cloud\seata-server-1.4.2\conf\registry.conf 8 17:30:54.166 INFO --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited 9 17:30:54.588 INFO --- [ main] i.s.core.rpc.netty.NettyServerBootstrap : Server started, listen port: 8092
3. 创建 Seata 客户端
本文在 “ Springcloud基础知识(16)- Spring Cloud Alibaba Seata (二) | 事务分组 ” 里 SpringcloudDemo05 项目基础上,创建 SeataNacosClient 子模块。
1) 创建 SeataNacosClient 子模块
选择左上的项目列表中的 SpringcloudDemo05,点击鼠标右键,选择 New -> Module 进入 New Module 页面:
Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next
Name: SeataNacosClient
GroupId: com.example
ArtifactId: SeataNacosClient
-> Finish
2) 修改 pom.xml,内容如下
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 5 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 6 <parent> 7 <artifactId>SpringcloudDemo05</artifactId> 8 <groupId>com.example</groupId> 9 <version>1.0-SNAPSHOT</version> 10 </parent> 11 <modelVersion>4.0.0</modelVersion> 12 13 <artifactId>SeataNacosClient</artifactId> 14 15 <name>SeataNacosClient</name> 16 <!-- FIXME change it to the project's website --> 17 <url>http://www.example.com</url> 18 19 <properties> 20 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 21 <maven.compiler.source>1.8</maven.compiler.source> 22 <maven.compiler.target>1.8</maven.compiler.target> 23 <maven.install.skip>true</maven.install.skip> 24 </properties> 25 26 <dependencies> 27 <dependency> 28 <groupId>junit</groupId> 29 <artifactId>junit</artifactId> 30 <scope>test</scope> 31 </dependency> 32 33 <dependency> 34 <groupId>org.springframework.boot</groupId> 35 <artifactId>spring-boot-starter-web</artifactId> 36 </dependency> 37 <dependency> 38 <groupId>org.springframework.boot</groupId> 39 <artifactId>spring-boot-starter-test</artifactId> 40 <scope>test</scope> 41 </dependency> 42 43 <!-- nacos --> 44 <dependency> 45 <groupId>com.alibaba.cloud</groupId> 46 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> 47 </dependency> 48 <dependency> 49 <groupId>com.alibaba.cloud</groupId> 50 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> 51 </dependency> 52 <!-- seata --> 53 <dependency> 54 <groupId>com.alibaba.cloud</groupId> 55 <artifactId>spring-cloud-starter-alibaba-seata</artifactId> 56 <!-- Spring cloud 2021.1 自动导入的 seata 版本是 1.3.0 --> 57 <exclusions> 58 <exclusion> 59 <groupId>io.seata</groupId> 60 <artifactId>seata-spring-boot-starter</artifactId> 61 </exclusion> 62 </exclusions> 63 </dependency> 64 <dependency> 65 <groupId>io.seata</groupId> 66 <artifactId>seata-spring-boot-starter</artifactId> 67 <version>1.4.2</version> 68 </dependency> 69 70 </dependencies> 71 72 </project> 73
注:这里我们用 seata 1.4.2 版本替换自动导入的 seata 1.3.0 版本,是因为下文需要用到 seata 1.4.2 的导入单个 dataId 配置的功能。
3) 访问 Nacos 页面创建 seataClient.properties
浏览器访问 http://localhost:8848/nacos/, 输入登录名和密码(默认 nacos/nacos),点击提交按钮,跳转到 Nacos Server 控制台页面。
在 Nacos Server 控制台的 “配置管理” 下的 “配置列表” 中,点击 “+” 按钮,新建如下配置。
1 Data ID: seataClient.properties 2 Group: SEATA_GROUP 3 配置格式: Properties 4 配置内容: 5 6 service.vgroupMapping.default_tx_group=default 7 service.default.grouplist=127.0.0.1:8092
注:可以把这两条内容直接加入到 seataServer.properties,无需新创建 seataClient.properties。这里分开放置 server 和 client 的配置,可以避免混淆两者的配置。
4) 创建 src/main/resources/application.yml 文件
1 server: 2 port: 4002 # 端口号 3 4 spring: 5 application: 6 name: seata-nacos-client-4002 # 服务名 7 cloud: 8 nacos: 9 discovery: 10 server-addr: 127.0.0.1:8848 11 namespace: # 留空表示使用 public 12 group: SEATA_GROUP 13 username: nacos 14 password: nacos 15 config: 16 server-addr: ${spring.cloud.nacos.discovery.server-addr} 17 context-path: /nacos 18 namespace: # 留空表示使用 public 19 username: ${spring.cloud.nacos.discovery.username} 20 password: ${spring.cloud.nacos.discovery.password} 21 22 seata: 23 #enabled: true 24 application-id: ${spring.application.name} 25 tx-service-group: default_tx_group 26 registry: 27 type: nacos 28 nacos: 29 server-addr: ${spring.cloud.nacos.discovery.server-addr} 30 #application: seata-server 31 group: ${spring.cloud.nacos.discovery.group} 32 namespace: # 留空表示使用 public 33 username: ${spring.cloud.nacos.discovery.username} 34 password: ${spring.cloud.nacos.discovery.password} 35 config: 36 type: nacos 37 nacos: 38 server-addr: ${spring.cloud.nacos.discovery.server-addr} 39 group: ${spring.cloud.nacos.discovery.group} 40 namespace: 41 username: ${spring.cloud.nacos.discovery.username} 42 password: ${spring.cloud.nacos.discovery.password} 43 dataId: seataClient.properties
配置说明:
a) 获取事务分组(服务启动时加载配置): SeataNacosClient 读取 application.yml 里 seata.tx-service-group 的值 “default_tx_group” (默认值,有的版本使用 "my_test_tx_group");
注:若 application.yml 里没有配置 seata.tx-service-group 项,自动以 spring.application.name的值 + "-seata-service-group" 拼接后的字符串作为分组名。
此时 Nacos 上 seataClient.properties 的配置应该如下。
service.vgroupMapping.seata-nacos-client-4002-seata-service-group=default
service.default.grouplist=127.0.0.1:8092
b) 查找 TC 集群名 (clusterName): SeataNacosClient 读取 Nacos 上 seataClient.properties 里的配置 service.vgroupMapping.default_tx_group 的值 "default",即 TC 集群名 (clusterName)。
c) 查询 TC 服务: SeataNacosClient 读取 seata.service.grouplist.default 的值 “127.0.0.1:8092”,即 TC 服务真实地址。
5) 修改 src/main/java/com/example/App.java 文件
1 package com.example; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 7 @EnableDiscoveryClient 8 @SpringBootApplication 9 public class App { 10 public static void main(String[] args) { 11 SpringApplication.run(App.class, args); 12 } 13 }
6) 运行 SeataNacosClient
菜单 Run -> Edit Configurations (或工具条上选择) —> 进入 Run/Debug Configurations 页面 -> Click "+" add new configuration -> Select "Maven":
Working directory: SeataNacosClient 所在路径
Command line: clean spring-boot:run
-> Apply / OK
点击 Run "SeataNacosClient [clean, spring-boot:run]" ,控制台输出如下:
1 INFO 21840 --- [ main] com.example.App : Started App in 3.316 seconds (JVM running for 3.619) 2 INFO 21840 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.0.2:8092 3 INFO 21840 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.0.2:8092 4 INFO 21840 --- [eoutChecker_2_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:RMROLE,address:192.168.0.2:8092,msg:< RegisterRMRequest{resourceIds='null', applicationId='seata-nacos-client-4002', transactionServiceGroup='default_tx_group'} > 5 INFO 21840 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.0.2:8092,msg:< RegisterTMRequest{applicationId='seata-nacos-client-4002', transactionServiceGroup='default_tx_group'} > 6 INFO 21840 --- [eoutChecker_2_1] i.s.c.rpc.netty.RmNettyRemotingClient : register RM success. client version:1.4.2, server version:1.4.2,channel:[id: 0xad4ddb2e, L:/192.168.0.2:49737 - R:/192.168.0.2:8092] 7 INFO 21840 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.4.2, server version:1.4.2,channel:[id: 0x513b3417, L:/192.168.0.2:49736 - R:/192.168.0.2:8092] 8 INFO 21840 --- [eoutChecker_2_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 92 ms, version:1.4.2,role:RMROLE,channel:[id: 0xad4ddb2e, L:/192.168.0.2:49737 - R:/192.168.0.2:8092] 9 INFO 21840 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 92 ms, version:1.4.2,role:TMROLE,channel:[id: 0x513b3417, L:/192.168.0.2:49736 - R:/192.168.0.2:8092]
注:使用 spring-cloud-starter-alibaba-seata 或 seata-spring-boot-starter 的 seata 客户端默认是开启状态 (可以设置 seata.enabled=false 来关闭)。
seata 客户端里包含了一个全局事务扫描器 (GlobalTransactionScanner),seata 客户端运行后(30 秒左右)GlobalTransactionScanner 会调用初始化功能,使用 netty 连接 Seata 服务端。
从 log 可以看出 SeataNacosClient 成功连接到了 Seata Server (192.168.0.2:8092),192.168.0.2 是本地主机的内网地址。