天天看点

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

背景:现有项目为springcloud+nacos 的。但是没有分布式事务处理机制,偶发数据问题,现需要引入seata进行全局事务管理。简单记录一下改造和学习过程,过一段时间自己100%会忘的一干二净,并没有对其进行很深的研究。

前期准备工作:

seata的模式介绍之类的,可以参考大佬的文章或者官网

https://wu55555.blog.csdn.net/article/details/124535548

seata官网:Seata 快速开始

官网现成samples,拿来直接能用的: GitHub - seata/seata-samples: seata-samples

服务下载地址:Releases · seata/seata · GitHub

我一开始就就是拿着官网案例里面的 springcloud-nacos-seata,直接本地运行起来,观察了一下具体的数据库变化之类的。

改造过程:

        官网案例毕竟比较简单也不适用生产环境,需要改造一下。毕竟现有项目已经运行一年多了,直接使用AT模式,简单快速。

        以1.4.2为例,目前最新为(1.6.0),大致参数都差不多,只是配置文件的形式有所区别。具体有什么优缺点并没有去仔细了解过。

一:seata服务端 修改conf下的registry.conf文件

        因为使用的nacos为配置中心,file.conf文件可以直接无视,最开始接触seata的时候看了很多很多的文章,明明用的nacos为配置中心,确非要引导读者去改fiel.conf里面的mode = "db",这压根没用好吧,一堆的坑。还有类似tx-service-group的问题也是一样。只有当配置里面用用fle的时候,改file.conf才会有效!!!

        如果需要使用file为配置中心,那个config下面的type请改成 type="file",此时seataserver启动的时候才会去读conf文件下面的file.conf。

registry.conf如下,基本修改nacos就行

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
	application = "seata-server"
	group = "SEATA_GROUP"
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    cluster = "default"
	username = "nacos"
    password = "nacos"
  }
  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"
    aclToken = ""
  }
  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 {
	application = "seata-server"
	group = "SEATA_GROUP"
    serverAddr = "127.0.0.1:8848"
    namespace = "24b8ffe7-7061-47b6-a2ef-4b860fd23b7f"
    cluster = "default"
	username = "nacos"
    password = "nacos"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  apollo {
    appId = "seata-server"
    ## apolloConfigService will cover apolloMeta
    apolloMeta = "http://192.168.1.204:8801"
    apolloConfigService = "http://192.168.1.204:8080"
    namespace = "application"
    apolloAccesskeySecret = ""
    cluster = "seata"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
    nodePath = "/seata/seata.properties"
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}
           

二、把seata的参数配置上传到nacos

        因为使用nacos为配置中心,那么你得告诉nacos,你需要怎么配置。

1、启动nacos,建立一个命名空间,存放seata的各项配置,我这边名称就是seata。命名空间id后续第3步要用到。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

 2、在下载下来的服务端里面,找到script\config-center\config.txt文档。如果没有,可以直接去官网找:seata/config.txt at 1.4.2 · seata/seata · GitHub

然后修改里面的配置,主要改动为mysql的相关配置和vgroupMapping的相关配置。当然,你也可以不改,后续去nacos上改。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 
现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

具体配置如下

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
#修改service.vgroupMapping.后面的字符串为自定义服务seata-group
service.vgroupMapping.seata_demo_group=default
service.default.grouplist=127.0.0.1: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.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
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.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=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.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
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
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.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
           

3、在script\config-center\nacos文件夹下面,找到上传脚本 nacos-config.sh,执行命名,上传配置,注意需要把下面 命名空间ID替换成你自己的,就是之前咱们第一步新增的时候生成的。

sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 命名空间ID -u nacos -w nacos
           

Windows下可以直接使用git 的 git Bash Here功能,也可以使用别的工具。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

如果出现以下情况,则是没有按第2步的时候放好,config.txt文件,按提示的目录,挪过去就行。其实上传脚本和config.txt的位置随意调整都行,也可以自己去nacos-config.sh修改具体指向config.txt的路径,只要能上传就好。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 
现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

上传成功如下

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

同时nacos中也能看到所有的配置项。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

后续要改配置都可以直接去nacos里面调整,方便快捷。例如,我本地数据库密码我从root改成了mysql,直接去nacos里配置列表seata下面,找到store.db.password这一项,编辑在发布就可以了。

4、调整各个client服务模块

        首先引入依赖,seata-spring-boot-starter不能在spring-cloud-starter-alibaba-seata之前。否则会出现feign调用的时候,事务不会滚的情况。大致使用因为feign调用是需要传递一个xid,传递xid的功能是只在spring-cloud-starter-alibaba-seata才有,seata-spring-boot-starter是没有传递xid的功能,会导致被调用的服务获取不到xid,从而无法触发事务回滚。

<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>1.4.2</version>
</dependency>        
           

然后配置文件里面添加,如下配置。

nacos相关地址信息改成自己的就可以了,需要注意的是tx-service-group和vgroup-mapping这两项,需要和你nacos里面配置的保持一致。否则RM和TM都没法注册到seata里面去。

application.yml添加seata相关配置

seata:
  tx-service-group: seata_demo_group
  registry:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.discovery.server-addr}
      username: ${spring.cloud.nacos.discovery.username}
      password: ${spring.cloud.nacos.discovery.password}
      group: ${spring.cloud.nacos.discovery.group}
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.config.server-addr}
      username: ${spring.cloud.nacos.config.username}
      password: ${spring.cloud.nacos.config.password}
      group: ${spring.cloud.nacos.config.group}
      namespace: 24b8ffe7-7061-47b6-a2ef-4b860fd23b7f
      dataId: seata-server.properties
  service:
    vgroup-mapping:
      seata_demo_group: default
           

可以去nacos查询vgroupMapping关键字。如果不配置这个的话,低版本的seata默认是my_test_tx_group,高版本的是default_tx_group,对应的值都是default。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

这个地方如果配置不对或者对应不上,那么各个client服务器启动都会弹出的

can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

例如这样的 

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

 5、建立seata 事务相关表

        因为我这边nacos中store.mode使用的是db模式,且用的是mysql数据库,那么就得建立 具体的表。建表语句可以在script\server\db下面找到。也可以去官网的github上找。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

这里copy一下mysql的。

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;
           

 因为需要使用AT模式,那么得在每个模块服务下的数据库里面添加undo_log表,具体sql如下

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 = 1
  DEFAULT CHARSET = utf8;
           

6、启动seata服务,其他其他模块服务、测试

        seata启动的时候,默认会先去找registry.conf里面的配置,然后再去找具体的file、nacos、eureka、redis之类的,具体看每个人咋配置的。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

各个模块启动成功后,在seataserver日志中可以看到TM和RM的注册信息,各个模块服务也有日志。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

在需要添加分布式事务的地方加上注解 @GlobalTransactional

测试前stock_tbl表数据如下

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

然后调用接口,自己断点调试就能发现,此时stock服务的deduct已经执行完成了

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

数据库已经发生修改。

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

undo_log会有记录

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

global_table等也会有记录

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

如果用的不是db而是file模式的话,可以直接去seata服务bin目录下去找相关全局事务记录信息(默认配置的前提下)

当抛出异常后,会发生回滚,在seataserver日志中可以看到如下信息

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

同时原来被修改的数据会发生回滚

现有项目集成seata的记录can not get cluster name in registry config ‘service.vgroupMapping.XXX‘ 

        以上全部为官网demo改造而成,基于1.4.2,目前实际环境用的是seata 1.6.0。大致差异为1.6.0的配置文件是个application.yml文件,基本配置都差不多。具体细节和差异性也没有去了解过,感兴趣的可以取官网查看。

可能遇到的问题

        模块服务启动报错can not get cluster name in registry config ‘service.vgroupMapping.XXX‘时,请检查模块服务的配置是否和注册中心的配置一样。参考上面第4点。

        配置成功,能启动,模块服务能注册seata,但是不生效。遇到异常不会回滚,可以参考第4点。是否依赖的问题或者xid为空的以及调用方和被调用方的xid是否一致等。

        能回滚,但是global_table、branch_table、lock_table没有任何数据变化,首先检查registry.conf里面config下的typ用的是啥,再到对应的配置下面去检查store.mode是不是db模式,以及数据库的配置信息了,只要有一项不对,就这几个表就不会有任何变化。例如registry.conf下的config使用nacos的话就要去nacos的配置中心去检查,使用file的话就得去file.conf文件去检查配置信息、其他的也是类似的。

        其他的问题可以参考官网Seata常见问题

继续阅读