使用Docker部署Seata,同时使用Nacos作为配置中心,如果在你了解了Nacos和Seata之后开始捣鼓整一个Demo来学习一下,那么你一定是看了Seata的官网的,而且还很有可能是看了Github上的seata和seata-samples项目的,然后开始在本机上把 springcloud-nacos-seata 这个项目尝试跑起来,很遗憾你没能轻易如愿。我之前也是一样,现在把项目真正跑起来的过程整理一遍。
我们就以Seata官网的 一个微服务示例 架构图
然后参照 seata-samples项目的 springcloud-nacos-seata实例代码,做一个梳理。
目标 :实现SpringCloud + Seata + Nacos + Mybatis + Druid + Mysql 整合。
说明: (1)其中 Seata 和 Nacos 均采用Docker 部署,且是单机模式,准确来说是docker-compose部署。 (2)Seata 和 Nacos 版本均是最新版,Seata-1.2.0, Nacos-1.3.0. Mysql 是5.7,我的用户名/密码:root/123456. (3)Seata和Nacos是部署在两个不同的容器中,所以不要想着你的localhost,而要用你的宿主机ip (我的IP是192.168.1.2 )
具体步骤: (1)由于使用Nacos作为配置中心,所以第一步是启动nacos这个比较容易,直接使用下面standalone-derby.yaml文件,这个文件你最好不要直接copy,因为你会发现它挂载了其它文件。它来源于 https://github.com/nacos-group/nacos-docker example文件夹,所以你需要先下载这个仓库。至于下面的prometheus和grafana你可以暂且不管,如果你之前学习Nacos就知道了就更好。
version: "2"
services:
nacos:
image: nacos/nacos-server:latest
container_name: nacos-standalone
environment:
- PREFER_HOST_MODE=hostname
- MODE=standalone
volumes:
- ./standalone-logs/:/home/nacos/logs
- ./init.d/custom.properties:/home/nacos/init.d/custom.properties
ports:
- "8848:8848"
prometheus:
container_name: prometheus
image: prom/prometheus:latest
volumes:
- ./prometheus/prometheus-standalone.yaml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
depends_on:
- nacos
restart: on-failure
grafana:
container_name: grafana
image: grafana/grafana:latest
ports:
- 3000:3000
restart: on-failure
Nacos启动之后你就可以验证了: http://localhost:8848/nacos/ 登录用户名和密码均是nacos
(2)由于Seata采用Nacos作为注册中心,所以在你启动Seata之前,你需要把配置写入到Nacos,当然Seata给你提供了一个shell脚本———nacos-config.sh,它来源于 https://github.com/seata/seata script文件夹,你只需要先配置好config.txt文件,然后一行命令就可以搞定。注意: config.txt和nacos-config.sh在同一目录下。原始的脚本在windows平台下可能需要修改一下,我会上传修改后的。
配置完成之后你可以在Nacos中验证了:找到->配置管理 -> 配置列表
当然上面是最后配置的效果,在此之前,我希望关注一下config.txt文件本身
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
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
service.vgroupMapping.my_test_tx_group=default
service.default.grouplist=192.168.1.2:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
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=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.logTable=undo_log
client.log.exceptionRate=100
store.mode=db
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.1.2:3306/seata?useUnicode=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
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.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9090
上面的配置是基于我的环境的,你可能需要做一些调整,我想强调几行分别是
(一)service.vgroupMapping.my_test_tx_group=default 说明my_test_tx_group 需要和代码中的tx-service-group一致
(二)store.mode=db store.db.datasource=druid store.db.url=jdbc:mysql://192.168.1.2:3306/seata?useUnicode=true store.db.user=root store.db.password=123456 说明上面采用db模式,Seata默认采用file模式,所以我们写db来覆盖,同时我们注意到url中有一个Schema是seata,没错我们一会需要在数据库中建好这个Schema同时我们注意到下面还有三张Table,我们一会一同建好。 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.lockTable=lock_table
(三) 既然说到了数据库,那么我们顺便把实例微服务中的数据库也建好,主要是三个Schema,每一个Schema中都有一张业务表和undo_log表。可以先看一下
上面数据库的sql文件其实分为两个部分,一部分是Seata Server作为db模式所需要的seata库(branch_table,global_table,lock_table)及Seata Client (分别是account,order,storage)各自业务表及一张undo_log表。第一部分sql语句可以在Seata的下载文件中找到
第二部分可以在 https://github.com/seata/seata-samples/tree/master/springcloud-nacos-seata
(3)在数据库准备工作完毕后,我们使用docker-compose.yml启动Seata服务器
version: "3.1"
services:
seata-server:
image: seataio/seata-server:latest
hostname: seata-server
ports:
- 8091:8091
volumes:
- ./config:/root/seata-config
environment:
- SEATA_PORT=8091
- SEATA_IP=192.168.1.2
- SEATA_CONFIG_NAME=file:/root/seata-config/registry
上面的配置中我们重点关注最后两行 ,一个是指定了IP,一个是指定了配置文件的位置。然后我们看一下配置文件——registry.conf。
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
serverAddr = "192.168.1.2"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = 0
password = ""
cluster = "default"
timeout = 0
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "192.168.1.2"
namespace = ""
group = "SEATA_GROUP"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
上面配置中 registry.type=nacos, config.type=nacos, nacos.serverAddr=”192.168.1.2″ nacos.group= “SEATA_GROUP” (这个和Nacos中相匹配)
(4)我们首先来看一下项目结构。
重点说明: (一)讲道理我们也是需要把account服务加上去的,但是用来验证分布式事务Seata的使用情况下,我们用两个就可以了。 (二)上面最主要的registry.conf文件和bootstrap.yml中spring.cloud.alibaba.seata.tx-service-group: ‘my_test_tx_group’(要和前面config.txt中 service.vgroupMapping.my_test_tx_group=default 相呼应)。 (三)再看registry.conf中最重要的部分:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "192.168.1.2"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
# 为了节省空间,其余type 省略
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "192.168.1.2"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
}
# 为了节省空间,其余type 省略
}
上面的application = “seata-server”和Nacos配置列表中保持一致
5.启动项目开始测试
(1)分布式事务成功,模拟正常下单、扣库存localhost:9091/order/placeOrder/commit
(2)分布式事务失败,模拟下单成功、扣库存失败,最终同时回滚 localhost:9091/order/placeOrder/rollback
后记 :这次整合Seata和Nacos的确花了一些时间,主要的原因是因为问题>人,自己能量级不够,所以这个问题才是问题,根本原因是因为没有理清思路,在众多配置文件和纷繁的零碎资料中走进了“问题森林”,会重点去查看一棵树,比如调试本地启动脚本jvm参数导致问题,配置文件中某项具体选项同时变化多个等等细节,而忘了初心是找到一个方向迅速走出这片森林。理思路,找方向,这件事一定要在头脑清晰的时候去做,头脑不清晰的时候只能算是“糊涂探索”。
最后希望这篇文章对你有帮助,我会把相关的脚本上传到我的github ,欢迎clone.