使用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.