Nacos nacos 黄文胜 2024-10-28 2024-10-31 一、安装nacos 官网:https://nacos.io/zh-cn/docs/what-is-nacos.html
1.1 Nacos
概述 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
1.2 下载nacos
下载最新稳定版2.1.1:https://github.com/alibaba/nacos/releases 后解压成目录 /dev/nacos
。
目录说明:
nacos |—bin 启动/停止 nacos 服务的shell脚本, widowns系统是 cmd脚本 |—conf nacos应用的配置文件,application.properties
配置文件; application.properties.example
配置示例,cluster.conf.example
集群示例配置,数据库脚本
|—logs 运行日志 |—data |—config-data 存放导入到nacos中的配置文件信息,一些目录中会生成相应的配置组目录。 |—target nacos 服务器的 jar 包
1.3 修改nacos
配置 修改 /dev/nacos/conf/application.properties
文件为以下内容, 可参照nacos
提供的样例文件application.properties.example
中的内容来修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 nacos.inetutils.ip-address =127.0.0.1 spring.datasource.platform =mysql db.num =1 db.url.0 =jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0 =root db.password.0 =root
1.4 创建nacos_config
数据库
新建 mysql 数据库:nacos_config
数据库编码:utf8mb4
,排序规则选:utf8_bin
创建/dev/nacos/conf/nacos-mysql.sql
中的表
1.5 启动nacos
注册配置中心 1 2 3 4 5 6 7 8 cd /dev/nacos/bin # Linux/macOS 启动脚本:'startup.sh' # Windows启动脚本'startup.cmd' sh startup.sh -m standalone # Linux/macOS 停止服务脚本:'shutdown.sh' ,windows停止服务脚本'shutdown.cmd' sh shutdown.sh
1.6 查看nacos
实事日志 新开一个命令窗口中执行,可以实现实时查看nacos 运行日志。
1 2 cd /dev/nacos/logs/ tail -f start.out
1.7 端点服务检查 spring-cloud-starter-alibaba-nacos-discovery 在实现的时候提供了一个 EndPoint, EndPoint 的访问地址为 http://ip:port/actuator/nacos-discovery
。 EndPoint 的信息主要提供了两类:
1、subscribe: 显示了当前有哪些服务订阅者 2、NacosDiscoveryProperties: 显示了当前服务实例关于 Nacos 的基础配置 通过浏览器访问 http://localhost:8081/actuator/nacos-discovery
你会在浏览器上看到。
1.8 配置加载优先级 不同方式配置加载优先级:
Nacos 配置中心目前提供以下三种配置能力从 Nacos 拉取相关的配置,当三种方式共同使用时,他们的一个优先级关系是:A < B < C:
A:通过 spring.cloud.nacos.config.shared-configs[n].data-id 支持多个共享 Data Id 的配置
B:通过 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多个扩展 Data Id 的配置
C:通过内部相关规则(spring.cloud.nacos.config.prefix 、spring.cloud.nacos.config.file-extension 、spring.cloud.nacos.config.group )自动生成相关的 Data Id 配置
在 Nacos Spring Cloud 中,dataId
的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
prefix
默认为 spring.application.name
的值,也可以通过配置项 spring.cloud.nacos.config.prefix
来配置。
spring.profiles.active
即为当前环境对应的 profile,详情可以参考 Spring Boot文档 。 注意:当 spring.profiles.active
为空时,对应的连接符 -
也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
file-exetension
为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension
来配置。目前只支持 properties
和 yaml
类型。
1.9 Nacos Starter 更多配置项信息
配置项
Key
默认值
说明
服务端地址
spring.cloud.nacos.discovery.server-addr
无
Nacos Server 启动监听的ip地址和端口
服务名
spring.cloud.nacos.discovery.service
${spring.application.name}
给当前的服务命名
权重
spring.cloud.nacos.discovery.weight
1
取值范围 1 到 100,数值越大,权重越大
网卡名
spring.cloud.nacos.discovery.network-interface
无
当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址
注册的IP地址
spring.cloud.nacos.discovery.ip
无
优先级最高
注册的端口
spring.cloud.nacos.discovery.port
-1
默认情况下不用配置,会自动探测
命名空间
spring.cloud.nacos.discovery.namespace
无
常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
AccessKey
spring.cloud.nacos.discovery.access-key
无
当要上阿里云时,阿里云上面的一个云账号名
SecretKey
spring.cloud.nacos.discovery.secret-key
无
当要上阿里云时,阿里云上面的一个云账号密码
Metadata
spring.cloud.nacos.discovery.metadata
无
使用 Map 格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息
日志文件名
spring.cloud.nacos.discovery.log-name
无
接入点
spring.cloud.nacos.discovery.enpoint
UTF-8
地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
是否集成 Ribbon
ribbon.nacos.enabled
true
一般都设置成 true 即可
#二、安装 seata
seata官方文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html
2.1 Seata 是什么? Seata 是一款 alibaba 开源的分布式事务解决方案
,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式
,为用户打造一站式的分布式解决方案。
Seata的整个过程模型:
TM : 事务的发起者。用来告诉TC,全局事务的开始,提交,回滚。
RM : 具体的事务资源,每一个RM都会作为一个分支事务注册在TC。
TC : 事务的协调者。也可以看做是Fescar-server,用于接收我们的事务的注册,提交和回滚。
2.2 下载 seata 下载最新稳定版1.5.2:下载中心 (seata.io)
解压到 /dev/
目录不用带版本号:/dev/seata
目录说明:
seata |—bin 启动 seata 服务的shell脚本, widowns系统是 cmd脚本 |—conf seata应用的配置的日志文件,application.yml
配置文件, application.example.yml
示例配置 |—ext |—lib seata 应用类库,包括 JDBC 驱动 |—logs 运行日志 |—script |—config-center 导入到相应注册中心(nacos, etcd3, apollo, zk等 )的shell脚本 |—target seata 应用的 jar 包
2.2 修改 seata 配置 在nacos 创建 seata 配置 进入到nacos
管理控制台后 -> 配置管理,点击页面右上角的+
号新建配置
数据 ID: seataServer.properties
在 Nacos Server 中,配置的 dataId(即 Data ID)的完整格式如下:
1 2 # 微服务的服务名[-当前环境对应的Profile].yaml 或 properties$ {prefix}-$ {spring.profiles.active}.$ {file-extension}
dataId 格式中各参数说明如下:
${prefix}
:默认取值为微服务的服务名,即配置文件中 spring.application.name
的值,我们可以在配置文件中通过配置 spring.cloud.nacos.config.prefix
来指定。
${spring.profiles.active}
:表示当前环境对应的 Profile,例如 dev、test、prod 等。当没有指定环境的 Profile 时,其对应的连接符也将不存在,dataId 的格式变成${prefix}.${file-extension}
。
${file-extension}
:表示配置内容的数据格式,我们可以在配置文件中通过配置项 spring.cloud.nacos.config.file-extension
来配置,例如 properties 和 yaml 。
事务组:SEATA_GROUP
名字空间默认:default
配置内容可通过 seata
提供的默认文件/dev/seata/script/config-center/config.txt
复制,因为我们将使用数据库db
模式存储seata
分布式事务的信息。所以可以把config.txt
文件中其它的存储方式删除。重点修改第69~ 71 行,注释73 行(store.publicKey),84~97 行
修改为db模式
,可以把file
、redis
模式的配置删除。
以下为修改好的config.txt
内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html #Transport configuration, for client and server transport.type=TCP transport.server=NIO transport.heartbeat=true transport.enableTmClientBatchSendRequest=false transport.enableRmClientBatchSendRequest=true transport.enableTcServerBatchSendResponse=false transport.rpcRmRequestTimeout=30000 transport.rpcTmRequestTimeout=30000 transport.rpcTcRequestTimeout=30000 transport.threadFactory.bossThreadPrefix=NettyBoss transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler transport.threadFactory.shareBossWorker=false transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector transport.threadFactory.clientSelectorThreadSize=1 transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread transport.threadFactory.bossThreadSize=1 transport.threadFactory.workerThreadSize=default transport.shutdown.wait=3 transport.serialization=seata transport.compressor=none #Transaction routing rules configuration, only for the client service.vgroupMapping.default_tx_group=default #service.vgroupMapping.cloud_user_tx_group=default #If you use a registry, you can ignore it service.default.grouplist=127.0.0.1:8091 service.enableDegrade=false service.disableGlobalTransaction=false #Transaction rule configuration, only for the client client.rm.asyncCommitBufferLimit=10000 client.rm.lock.retryInterval=10 client.rm.lock.retryTimes=30 client.rm.lock.retryPolicyBranchRollbackOnConflict=true client.rm.reportRetryCount=5 client.rm.tableMetaCheckEnable=true client.rm.tableMetaCheckerInterval=60000 client.rm.sqlParserType=druid client.rm.reportSuccessEnable=false client.rm.sagaBranchRegisterEnable=false client.rm.sagaJsonParser=fastjson client.rm.tccActionInterceptorOrder=-2147482648 client.tm.commitRetryCount=5 client.tm.rollbackRetryCount=5 client.tm.defaultGlobalTransactionTimeout=60000 client.tm.degradeCheck=false client.tm.degradeCheckAllowTimes=10 client.tm.degradeCheckPeriod=2000 client.tm.interceptorOrder=-2147482648 client.undo.dataValidation=true client.undo.logSerialization=jackson client.undo.onlyCareUpdateColumns=true server.undo.logSaveDays=7 server.undo.logDeletePeriod=86400000 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k #For TCC transaction mode tcc.fence.logTableName=tcc_fence_log tcc.fence.cleanPeriod=1h #Log rule configuration, for client and server log.exceptionRate=100 #Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional. store.mode=db store.lock.mode=db store.session.mode=db #Used for password encryption #store.publicKey= #These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block. store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://127.0.0.1:23306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user=root store.db.password=root 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 #Transaction rule configuration, only for the server server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTimeoutUnlockEnable=false server.distributedLockExpireTime=10000 server.xaerNotaRetryTimeout=60000 server.session.branchAsyncQueueSize=5000 server.session.enableBranchAsyncRemove=false #Metrics configuration, only for the server metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898
同时把修改好的 config.txt
文件复制一份到bin目录中:/dev/seata/bin/config.txt
在 nacos
创建中seataServer.properties
(数据 ID)配置是为了便于将来在各springcloud 客户端应用中引用seata 配置。
修改 seata 应用配置文件 seata 应用配置文件:$SEATA_HOME/conf/application.yml
application.yml
文件的内容可以参考 seata
提供的示例文件/dev/seata/conf/application.example.yml
来进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 server: port: 7091 spring: application: name: seata-server logging: config: classpath:logback-spring.xml file: path: ${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: type: nacos nacos: server-addr: 127.0 .0 .1 :8848 namespace: group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties registry: type: nacos nacos: application: seata-server server-addr: 127.0 .0 .1 :8848 group: SEATA_GROUP namespace: cluster: default username: nacos password: nacos server: service-port: 8091 enable-check-auth: true enable-parallel-request-handle: true retry-dead-threshold: 130000 recovery: handle-all-session-period: 1000 security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
2.3 创建 seata 数据库
新建数据库:seata
数据库编码:utf8mb4
,排序规则选:utf8_bin
创建/dev/seata/script/server/db/mysql.sql
中的表
2.4 启动 seata 分两步,如下:
(1)导入seata
配置到 nacos
先启动nacos
,把修改好的/dev/seata/script/config-center/config.txt
内容导入到 nacos 配置中心。
1 2 3 4 5 6 # 进入导入脚本目录 cd /dev/seata/script/config-center/nacos/ # 初始化seata 的nacos配置,执行导入脚本导入 config.txt 文件中的内容 sh nacos-config.sh 127.0.0.1 # sh /dev/seata/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u nacos -w nacos
(2)启动seata
1 2 3 cd /dev/seata/bin # 启动 seata 服务器, windows 用 seata-server.bat sh seata-server.sh -p 8091 -h 127.0.0.1 -m db
bin目录提供了一个空的startup.sh
文件,为方便以后启动seata
可以将上面启动命令写入到到startup.sh
文件中,以后只需在命令行运行:
(3)seata 管理控制台
在浏览器中打开:Seata
用户名:seata 密码:seata
三、springcloud 应用模块 本案例以电商系统的模块为例只给出关键部分的代码,这里演示了用户模块与商品模块
来说明如何使用 seata 分布式事务 AT 模式
,有关更其它的事务说明请查阅seata
官方文档 “各事务模式” 一节。
用户模块
账户模块
商品模块
订单模块
库存模块
文档末给出其它3 个模块的数据库表,自己可自行扩展,下面给出了模块间的调用关系。
用户模块 —下订单—> 订单模块 (订单数据库) (用户和账户数据库) |— 减库存—> 库存模块 (库存数据库) |—更新账户–> 账户模块 —查找商品 –>商品模块 (商品数据库)
这里仅仅为了说明如何在 springcloud 中使用seata 分布式事务
。
3.1 springcloud-product 模块 这里只给出了关键部分代码,其它代码与正常开发一样。
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.lanqiao.cloud</groupId > <artifactId > springcloud-product</artifactId > <version > 0.0.1</version > <name > springcloud-product</name > <description > 商品微服务</description > <properties > <java.version > 1.8</java.version > <spring-boot.version > 2.4.2</spring-boot.version > <spring-cloud-alibaba.version > 2021.1</spring-cloud-alibaba.version > <alibaba-seata.version > 1.5.2</alibaba-seata.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-nacos-config</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-nacos-discovery</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-seata</artifactId > <version > ${spring-cloud-alibaba.version}</version > <exclusions > <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 > ${alibaba-seata.version}</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > <dependency > <groupId > com.lanqiao.cloud</groupId > <artifactId > springcloud-domain</artifactId > <version > 1.0</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <version > ${spring-boot.version}</version > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > ${spring-boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-alibaba-dependencies</artifactId > <version > ${spring-cloud-alibaba.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.1</version > <configuration > <source > 1.8</source > <target > 1.8</target > <encoding > UTF-8</encoding > </configuration > </plugin > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > <configuration > <mainClass > com.lanqiao.cloud.product.SpringcloudProductApplication</mainClass > </configuration > </plugin > </plugins > </build > </project >
application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 server: port: 8000 spring: profiles: active: dev application: name: microservice-product cloud: loadbalancer: ribbon: enabled: false nacos: discovery: server-addr: 127.0 .0 .1 :8848 username: nacos password: nacos namespace: group: SEATA_GROUP datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/tbmall_product?useUnicode=true&autoReconnect=true username: root password: root type: com.alibaba.druid.pool.DruidDataSource ruid: max-active: 10 initial-size: 2 min-idle: 10 max-wait: 60000 test-on-borrow: false test-while-idle: true time-between-eviction-runs-millis: 300000 min-evictable-idle-time-millis: 1800000 validation-query: select 1 remove-abandoned-timeout: 30 remove-abandoned: true log-abandoned: true filter: slf4j: enabled: true statement-log-enabled: false result-set-log-enabled: false seata: enabled: true application-id: ${spring.application.name} tx-service-group: cloud_product_tx_group enable-auto-data-source-proxy: false data-source-proxy-mode: AT use-jdk-proxy: false client: rm: async-commit-buffer-limit: 1000 report-retry-count: 5 table-meta-check-enable: false report-success-enable: false lock: retry-interval: 10 retry-times: 30 retry-policy-branch-rollback-on-conflict: true tm: commit-retry-count: 5 rollback-retry-count: 5 undo: data-validation: true log-serialization: jackson log-table: undo_log service: vgroupMapping: default_tx_group: default grouplist: default: 127.0 .0 .1 :8091 transport: shutdown: wait: 3 thread-factory: boss-thread-prefix: NettyBoss worker-thread-prefix: NettyServerNIOWorker server-executor-thread-prefix: NettyServerBizHandler share-boss-worker: false client-selector-thread-prefix: NettyClientSelector client-selector-thread-size: 1 client-worker-thread-prefix: NettyClientWorkerThread worker-thread-size: default boss-thread-size: 1 type: TCP server: NIO heartbeat: true serialization: seata compressor: none enable-client-batch-send-request: true config: type: nacos nacos: serverAddr: 127.0 .0 .1 :8848 data-id: seataServer.properties group: SEATA_GROUP namespace: username: nacos password: nacos registry: type: nacos nacos: application: seata-server server-addr: 127.0 .0 .1 :8848 group: SEATA_GROUP namespace: cluster: default username: nacos password: nacos
DataID名称标识:
${spring.cloud.nacos.config.prefix}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
prefix:前缀,默认是 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置。
代理分布式数据源 因为整合了 Mybatis 需要对本地数据源进行代理,以便让本地数据源参与到seata全局分布式事务
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.lanqiao.cloud.product;import com.alibaba.druid.pool.DruidDataSource;import io.seata.rm.datasource.DataSourceProxy;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.transaction.SpringManagedTransactionFactory;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;@Configuration public class ProductATDataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource druidDataSource () { return new DruidDataSource (); } @Bean("dataSourceProxy") public DataSource dataSource (DruidDataSource druidDataSource) { return new DataSourceProxy (druidDataSource); } @Bean public SqlSessionFactory sqlSessionFactory (DataSource dataSourceProxy) throws Exception{ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean (); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver () .getResources("classpath*:/mapper/*.xml" )); sqlSessionFactoryBean.setTypeAliasesPackage("om.lanqiao.cloud.domain.product" ); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory ()); return sqlSessionFactoryBean.getObject(); } }
ProductService 服务类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Service public class ProductService { @Autowired ProductMapper productMapper; public List<Product> selectAll () { return productMapper.selectAll(); } @Transactional(rollbackFor = {Exception.class}) public int updateByPrimaryKeySelective (Product record) { return this .productMapper.updateByPrimaryKeySelective(record); } }
SQL脚本 和 undo_log表 在每个模块的数据库中都要创建一个undo_log
表,表结构完全一样,此表用来给seata全局分布事务
中用来记录回滚日志。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 CREATE TABLE `mall_products` ( `product_no` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '主键,商品编号' , `product_name` varchar (50 ) NOT NULL , `product_price` double (6 ,1 ) NOT NULL DEFAULT '0.0' , PRIMARY KEY (`product_no`) ) ENGINE= InnoDB AUTO_INCREMENT= 3 DEFAULT CHARSET= utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT= '商城商品表' ; ########### 回滚日志 CREATE TABLE `undo_log` ( `id` bigint (20 ) NOT NULL AUTO_INCREMENT, `branch_id` bigint (20 ) NOT NULL , `xid` varchar (100 ) NOT NULL , `context` varchar (128 ) NOT NULL , `rollback_info` longblob NOT NULL , `log_status` int (11 ) NOT NULL , `log_created` datetime NOT NULL , `log_modified` datetime NOT NULL , `ext` varchar (100 ) DEFAULT NULL , PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE= InnoDB AUTO_INCREMENT= 4 DEFAULT CHARSET= utf8;
3.2 springcloud-user 模块 这里只给出了关键部分代码,其它代码与正常开发一样。配置文件和以前的稍有不同把原有的application.yml 文件拆分成:bootstrap.yml 与 application-dev.yml
springboot应用在启动加载配置的优级:bootstrap.yml > application[-profile].yml
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.lanqiao.cloud</groupId > <artifactId > springcloud-user</artifactId > <version > 0.0.1</version > <name > springcloud-user</name > <description > 用户服务</description > <properties > <java.version > 1.8</java.version > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <spring-boot.version > 2.4.2</spring-boot.version > <spring-cloud.version > 3.0.1</spring-cloud.version > <spring-cloud-alibaba.version > 2021.1</spring-cloud-alibaba.version > <alibaba-seata.version > 1.5.2</alibaba-seata.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-bootstrap</artifactId > <version > ${spring-cloud.version}</version > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-nacos-config</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-nacos-discovery</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-seata</artifactId > <version > ${spring-cloud-alibaba.version}</version > <exclusions > <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 > ${alibaba-seata.version}</version > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-loadbalancer</artifactId > <version > ${spring-cloud.version}</version > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > <version > ${spring-cloud.version}</version > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-hystrix</artifactId > <version > 2.2.10.RELEASE</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.2</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > <dependency > <groupId > com.lanqiao.cloud</groupId > <artifactId > springcloud-domain</artifactId > <version > 1.0</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > ${spring-boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-alibaba-dependencies</artifactId > <version > ${spring-cloud-alibaba.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.1</version > <configuration > <source > 1.8</source > <target > 1.8</target > <encoding > UTF-8</encoding > </configuration > </plugin > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > <configuration > <mainClass > com.lanqiao.cloud.user.SeataUserServiceApplication</mainClass > </configuration > </plugin > </plugins > </build > </project >
bootstrap.yml Spring Cloud Alibaba Nacos 作为配置中心时,通常会使用 bootstrap.yml
或 bootstrap.properties
文件来初始化 Nacos 相关的配置。这些配置文件会在 Spring Boot 应用启动时被加载,用来从 Nacos 中获取配置并覆盖本地的配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 server: port: 9999 spring: profiles: active: dev application: name: microservice-user main: allow-bean-definition-overriding: true cloud: nacos: discovery: server-addr: microservice-user namespace: ${spring.cloud.nacos.discovery.namespace} enabled: true register-enabled: true group: SEATA_GROUP config: server-addr: 127.0 .0 .1 :8848 namespace: ${spring.cloud.nacos.discovery.server-addr} file-extension: yaml group: SEATA_GROUP refresh-enabled: true shared-configs: - data-id: tbmall-user-mysql.yaml group: SEATA_GROUP refresh: true - data-id: common-redis.yaml group: SEATA_GROUP
spring.cloud.nacos.discovery.server-addr
: 这个地址用于 Nacos 服务发现和配置中心。
spring.cloud.nacos.config.file-extension
: 配置文件扩展名,这里设为 yml
。
spring.cloud.nacos.config.group
: 配置所在的组,默认是 DEFAULT_GROUP
。
spring.cloud.nacos.config.refresh-enabled
: 如果设置为 true
,那么应用可以在运行时刷新配置而不需要重启。
需要注意的是,当配置文件中的数据 ID(即配置文件名)未明确指定时,Spring Cloud Alibaba 会自动生成一个默认的数据 ID,通常是应用名称加上环境标志和文件扩展名。
application-dev.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 spring: application: name: microservice-user cloud: loadbalancer: ribbon: enabled: false seata: enabled: true application-id: ${spring.application.name} tx-service-group: cloud_user_tx_group enable-auto-data-source-proxy: false data-source-proxy-mode: AT use-jdk-proxy: false client: rm: async-commit-buffer-limit: 1000 report-retry-count: 5 table-meta-check-enable: false report-success-enable: false lock: retry-interval: 10 retry-times: 30 retry-policy-branch-rollback-on-conflict: true tm: commit-retry-count: 5 rollback-retry-count: 5 undo: data-validation: true log-serialization: jackson log-table: undo_log service: vgroupMapping: cloud_user_tx_group: default grouplist: default: 127.0 .0 .1 :8091 transport: shutdown: wait: 3 thread-factory: boss-thread-prefix: NettyBoss worker-thread-prefix: NettyServerNIOWorker server-executor-thread-prefix: NettyServerBizHandler share-boss-worker: false client-selector-thread-prefix: NettyClientSelector client-selector-thread-size: 1 client-worker-thread-prefix: NettyClientWorkerThread worker-thread-size: default boss-thread-size: 1 type: TCP server: NIO heartbeat: true serialization: seata compressor: none enable-client-batch-send-request: true config: type: nacos nacos: serverAddr: 127.0 .0 .1 :8848 data-id: seataServer.properties group: SEATA_GROUP namespace: username: nacos password: nacos registry: type: nacos nacos: application: seata-server server-addr: 127.0 .0 .1 :8848 group: SEATA_GROUP namespace: cluster: default username: nacos password: nacos feign: circuitbreaker: enabled: true
在nacos
配置中心新建配置 上面的bootstrap.yml
配置中有从nacos
中加载数据库配置,所以这一步要在nacos
中新建配置。如果把数据源配置直接写appplication-dev.yml
配置文件中,则可以省略这一步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/tbmall_user?useUnicode=true&autoReconnect=true username: root password: root type: com.alibaba.druid.pool.DruidDataSource ruid: max-active: 10 initial-size: 2 min-idle: 10 max-wait: 60000 test-on-borrow: false test-while-idle: true time-between-eviction-runs-millis: 300000 min-evictable-idle-time-millis: 1800000 validation-query: select 1 remove-abandoned-timeout: 30 remove-abandoned: true log-abandoned: true filter: slf4j: enabled: true statement-log-enabled: false result-set-log-enabled: false
代理数据源 新建类UserATDataSourceConfiguration
与springcloud-product
模块的代码一样,复制过来改一下。
因为整合了 Mybatis 需要对本地数据源进行代理,以便让本地数据源参与到seata全局分布式事务
中。只需修改一下代码中的包名即可。
IUserService 接口 & UserServiceImpl 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface IUserService { int updateByPrimaryKey (User record) ; } @Service("userService") public class UserServiceImpl implements IUserService { @Autowired UserMapper userMapper; @Override @Transactional(rollbackFor = Exception.class) public int updateByPrimaryKey (User record) { return userMapper.updateByPrimaryKey(record); } }
业务逻辑处理(接口、实现) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public interface BusinessService { List<Product> findAllProducts () ; Boolean update (User user, Product product) ; } package com.lanqiao.cloud.user.service;import com.lanqiao.cloud.domain.product.Product;import com.lanqiao.cloud.domain.user.User;import com.lanqiao.cloud.user.feignclient.ProductServiceFeignClient;import io.seata.core.context.RootContext;import io.seata.spring.annotation.GlobalTransactional;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service("businessService") @Slf4j public class BusinessServiceImpl implements BusinessService { @Autowired ProductServiceFeignClient productServiceFeignClient; @Autowired IUserService userService; @Override public List<Product> findAllProducts () { return productServiceFeignClient.findAllProducts(); } @Override @GlobalTransactional(name = "fsp-usr-product",rollbackFor = {Exception.class, RuntimeException.class}) public Boolean update (User user, Product product) { String xid = RootContext.getXID(); log.info("New Transaction Begins: " + xid); Boolean b1 = productServiceFeignClient.updateProduct(product); log.info("远程调用更新商品服务:{}" ,b1); if (!b1) { throw new RuntimeException ("商品远程服务调用失败,事务回滚!" ); } int i = userService.updateByPrimaryKey(user); log.info("本地更新用户:{}" , i==1 ); if (product.getProductPrice()>5000.0 ) { throw new RuntimeException ("用户更新失败,事务回滚!" ); } return b1 && (i==1 ); } }
SQL脚本 1 2 3 4 5 6 7 CREATE TABLE `mall_users` ( `user_id` int (11 ) NOT NULL AUTO_INCREMENT, `user_name` varchar (10 ) DEFAULT NULL , `password` varchar (10 ) NOT NULL , `age` int (3 ) DEFAULT NULL , PRIMARY KEY (`user_id`) ) ENGINE= InnoDB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8mb4 COLLATE = utf8mb4_0900_ai_ci
其它模块的数据库表如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #########################tbmall_order库 use database tbmall_order; CREATE TABLE `mall_orders` ( `id` mediumint(11 ) NOT NULL AUTO_INCREMENT, `user_id` int (11 ) DEFAULT NULL , `product_id` int (11 ) DEFAULT NULL , `COUNT` int (11 ) DEFAULT NULL COMMENT '数量' , `pay_amount` decimal (10 ,2 ) DEFAULT NULL , `status` varchar (100 ) DEFAULT NULL , `add_time` datetime DEFAULT CURRENT_TIMESTAMP , `last_update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8 #########################tbmall_pay库 use database tbmall_pay; DROP TABLE mall_account;CREATE TABLE `mall_account` ( `id` BIGINT (11 ) NOT NULL AUTO_INCREMENT COMMENT 'id' , `user_id` BIGINT (11 ) DEFAULT NULL COMMENT '用户id' , `total` DECIMAL (10 ,0 ) DEFAULT NULL COMMENT '总额度' , `used` DECIMAL (10 ,0 ) DEFAULT NULL COMMENT '已用余额' , `balance` DECIMAL (10 ,0 ) DEFAULT '0' COMMENT '剩余可用额度' , `last_update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , PRIMARY KEY (`id`) ) ENGINE= INNODB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8; INSERT INTO `tbmall_pay`.`mall_account` (`id`, `user_id`, `total`, `used`, `balance`) VALUES ('1' , '1' , '1000' , '0' , '100' );#########################tbmall_storage库 use database tbmall_storage; CREATE TABLE `mall_storage` ( `id` BIGINT (11 ) NOT NULL AUTO_INCREMENT, `product_id` BIGINT (11 ) DEFAULT NULL COMMENT '产品id' , `total` INT (11 ) DEFAULT NULL COMMENT '总库存' , `used` INT (11 ) DEFAULT NULL COMMENT '已用库存' , `residue` INT (11 ) DEFAULT NULL COMMENT '剩余库存' , PRIMARY KEY (`id`) ) ENGINE= INNODB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8; INSERT INTO `tbmall_storage`.`mall_storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1' , '1' , '100' , '0' , '100' );