Seata 配置错误排查

作成日: 2024年12月31日

使用モデル O1 by Chat01

質問

我在使用seata配置时报错
2024-12-31 12:46:25.285 INFO 17064 --- [nio-8088-exec-1] io.seata.tm.TransactionManagerHolder : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@50937c39
io.seata.common.exception.FrameworkException: No available service
seata服务端的配置:
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作为配置中心
nacos:
server-addr: http://192.168.28.32:8848
namespace: public #命名空间配置
group: DEFAULT_GROUP #分组信息配置
username: nacos #如果开启了nacos的验证功能则需要配置
password: nacos
data-id: seataServer.properties #nacos中的配置文件名称
registry:
type: nacos #使用nacos作为注册中心
nacos:
application: seata-server
server-addr: http://192.168.28.32:8848
group: DEFAULT_GROUP
namespace: public
cluster: seatatest #此处注意,这的值要和nacos中的配置文件service.vgroupMapping.my_test_tx_group的值一样
username: nacos
password: nacos
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key: ""
#secret-key: ""
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,//*.css,//.js,/**/.html,//*.map,//.svg,/**/.png,//*.ico,/console-fe/public/,/api/v1/auth/login
使用了nacos作为配置注册中心,其中配置中心的配置:
#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=seatatest
#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=

#If store.mode,store.lock.mode,store.session.mode are not equal to file, you can remove the configuration block.
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

#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
#此处注意驱动版本mysql8后是需要加cj
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.28.28:3305/seata?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
store.db.user=admin
store.db.password=NWT@temp123
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
server.enableParallelRequestHandle=false

#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
springboot客户端的配置:
seata:
enabled: true
application-id: seata-server
enable-auto-data-source-proxy: false

事务组的名称,对应service.vgroupMapping.default_tx_group=xxx中配置的default_tx_group

tx-service-group: seatatest
service:
vgroup-mapping:
seatatest: default
group-list:
default: 192.168.28.28:8091 # 你的 Seata TC 服务器地址
disable-global-transaction: false
config:
type: nacos
nacos:
namespace: test-prod
server-addr: 192.168.28.32:8848
username: nacos
password: nacos
group: SEATA_GROUP
data-id: seataServer.properties
registry:
type: nacos
nacos:
application: seata-server # tc服务在nacos中的服务名称
cluster: seatatest
server-addr: 192.168.28.32:8848
group: SEATA_GROUP
namespace: test-prod
username: nacos
password: nacos
帮我优化修改这些配置

思考

Identifying configuration issues

OK, let's think it through: The server is using port 7091, but the client config mentions 192.168.28.28:8091. This mismatch needs correction to avoid communication issues.

Aligning configurations

Taking a closer look, the server-side config should match the client-side's seata.tx-service-group entry. This means setting service.vgroupMapping.seatatest=seatatest on the server side.

Aligning server-client configurations

I'm piecing together the server and client configs using nacos cluster seatatest. Ensuring the server-side config match the seata.tx-service-group entry, aligning server and environment settings are key.

Matching namespaces and groups

I'm working through aligning nacos namespaces and groups for both server and client sides. It's crucial they use the same namespace and group, and that data-id and cluster settings are consistent.

Aligning configurations

Setting up the server side with specific nacos settings, and synchronizing with the client side. Uniform namespace, group, and data-id to streamline communication.

Harmonizing configurations

I'm working through aligning database and nacos settings. Ensuring ports match and centralizing server and client configurations to streamline interactions. This makes me think there's a method to the madness.

Aligning configurations

I’m refining the server-side configuration to ensure it seamlessly connects with the client, using vgroupMapping for seatatest and setting port to 8091, keeping the cluster name consistent.

Filling in the details

Unifying NACOS group names, streamlining namespaces to "public", and setting data-id=seataServer.properties. Ensuring the server port is consistently 8091 for both server and client.

Ensuring consistency

I'm detailing the final server configuration: aligning nacos settings, setting the server port to 8091, and standardizing seata server details across namespace setups for seamless client-server interaction.

Thought about Seata配置优化,持续 22 秒

回答

下面这类 No available service 的报错,通常代表 Seata 客户端找不到可用的 Seata Server 服务。
这往往是由于 Seata Server 与 Seata Client 的配置不一致(比如:注册中心、命名空间、分组、vgroupMapping、端口等等)导致的。


1. 常见问题排查思路

  1. Seata Server 启动端口与 Client 引用的端口是否一致?

    • 比如 Seata Server 里 server.port: 7091,但 Client 却写了 192.168.28.28:8091
  2. Seata Server 与 Client 的 registry(注册中心)配置是否一致?

    • 例如:都使用 nacosserver-addr 是否相同;namespace 是否相同;group 是否相同;cluster 是否对应;username/password 是否相同。
  3. Seata Server 与 Client 的 config(配置中心)配置是否一致?

    • 都是 nacosnamespacegroupdata-id 等必须一致。
  4. service.vgroupMapping.xxxseata.tx-service-group=xxx 是否匹配?

    • 如果客户端的 seata.tx-service-group 设置成 seatatest,那在 Seata Server 的配置中就需要有 service.vgroupMapping.seatatest=xxxx(或类似映射)。

2. 优化后的整体思路

  • 尽量让 Seata Server 和 Seata Client 的配置文件中:
    1. nacos 的地址、命名空间、分组(group)等参数保持一致。
    2. Seata Server 监听的 端口 与 Client 侧的 group-list / cluster 里填写的端口保持一致。
    3. service.vgroupMapping.xxx 的键(xxx)要和 Client 侧 seata.tx-service-group=xxx 保持相同。
    4. 如果要用到 Nacos 做服务发现(registry),那么 Client 里就不一定要再写死 IP:PORT 到 service.group-list;只要保证 cluster 和 Nacos 上面的服务实例一致即可。
    5. 如果暂时还想保留直连方式,也要把 service.vgroupMapping.xxx=… 中的 grouplist 改成实际能访问到 Seata Server 的 IP:Port。

下文给出一套示例可行配置,可根据自己网络环境、数据源等微调。


3. Seata Server 侧示例配置

以下示例

  • 端口统一改为 8091(因为很多示例里习惯用 8091),如果你坚持用 7091,那 Client 那边也要写 7091
  • 使用 nacos 做注册中心和配置中心;
  • namespace 改为 test-prodgroup 改为 SEATA_GROUP
  • Data ID 依旧是 seataServer.properties
  • vgroupMapping 的键改为 seatatest,与 Client 侧保持一致;
  • 底层存储使用 DB,指向了 MySQL(示例中的 IP、端口、用户名请根据真实环境修改)。

3.1 application.yml(Server 侧)

yaml
server: port: 8091 spring: application: name: seata-server logging: config: classpath:logback-spring.xml file: path: ${user.home}/logs/seata console: user: username: seata password: seata seata: # -- 安全相关,可根据实际需要保留或修改 -- security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login # -- 配置中心 -- config: type: nacos nacos: server-addr: 192.168.28.32:8848 # 与客户端保持一致 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties # -- 注册中心 -- registry: type: nacos nacos: application: seata-server server-addr: 192.168.28.32:8848 group: SEATA_GROUP namespace: test-prod cluster: seatatest # 一定要和 data-id 里的 seatatest 一致 username: nacos password: nacos

3.2 seataServer.properties(Nacos 中的配置内容示例)

在 Nacos 上新建(或编辑) Data ID = seataServer.properties, Group = SEATA_GROUP, Namespace = test-prod,内容大致如下(可按需改动):

properties
########## transport ########## 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 ########## service ########## # 注意:这里与 Client 侧的 tx-service-group 要对应 service.vgroupMapping.seatatest=seatatest service.default.grouplist=127.0.0.1:8091 service.enableDegrade=false service.disableGlobalTransaction=false ########## 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 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k 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 ########## log ########## log.exceptionRate=100 ########## store ########## store.mode=db store.lock.mode=db store.session.mode=db # 数据源相关(示例为 MySQL) store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://192.168.28.28:3305/seata?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false store.db.user=admin store.db.password=NWT@temp123 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.lockTable=lock_table store.db.queryLimit=100 store.db.maxWait=5000 ########## 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 server.enableParallelRequestHandle=false ########## metrics ########## metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898

这里最关键的是:

  1. service.vgroupMapping.seatatest=seatatest,用于和客户端的 tx-service-group=seatatest 对应;
  2. service.default.grouplist=127.0.0.1:8091 仅在直连时才生效;如果走 Nacos 注册中心,可不用太关注这个值;
  3. store.db.urlstore.db.userstore.db.password 等需换成真实数据库信息。

4. Spring Boot Client 侧示例配置

假设在 application.yml 中这么写(或 application.properties 做同样含义配置):

yaml
seata: enabled: true application-id: seata-server # 注意这里要和 service.vgroupMapping.seatatest=xxx 对应 tx-service-group: seatatest enable-auto-data-source-proxy: false # 是否自动代理数据源,可按需调整 service: # 如果已经用 Nacos 注册中心,这里的 vgroup-mapping 可以简单对齐一下 vgroup-mapping: seatatest: seatatest disable-global-transaction: false # 如果你想强行直连,也可以这样写 # group-list: # seatatest: 192.168.28.28:8091 config: type: nacos nacos: # 与 Seata Server 一致 server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties registry: type: nacos nacos: application: seata-server cluster: seatatest server-addr: 192.168.28.32:8848 group: SEATA_GROUP namespace: test-prod username: nacos password: nacos

这样即可通过 Nacos 发现 Seata Server。
如果你确认不想通过 Nacos 服务发现,而是直连 Seata Server(假设 IP 是 192.168.28.28,端口是 8091),那么可以把

yaml
service: vgroup-mapping: seatatest: default group-list: default: 192.168.28.28:8091

之类的注释打开,但要确保与服务端 service.vgroupMapping.seatatest=seatatest键值匹配


5. 总结

  • 先统一好 Seata Server 的端口(8091 或者 7091),以及 Nacos 上的相关配置(namespace、group、data-id),两边必须一致。
  • service.vgroupMapping.xxx 中的 xxx 必须和 Client seata.tx-service-group=xxx 相同,否则客户端进来时不知道应该映射到哪个集群。
  • 如果打算 通过 Nacos 注册中心 方式去发现 Seata Server,就不需要显式再写死 group-list;但要保证 clusterapplication 这些信息也一致,且 Seata Server 能够正常注册到 Nacos。
  • 如果暂时不走 Nacos 注册中心(或者在调试阶段想简化),那就要保证 service.vgroupMapping.xxx=default + service.default.grouplist=SeataServerIP:SeataServerPort 这种直连配法,且客户端对应 tx-service-group=xxx + service.vgroup-mapping.xxx=default + group-list.default=SeataServerIP:SeataServerPort 匹配上。

经过以上调整后,一般就能避免 No available service 的问题了。若仍然出现类似错误,请重点检查:

  • Seata Server 是否真的启动成功?
  • Seata Server 是否在 Nacos 中注册成功?(在 Nacos 控制台看一下服务列表)
  • 客户端是否能够访问到 8091 端口?(防火墙、Docker 映射、K8s Service 等环境问题)

这样基本就能让 Seata 正常跑起来。祝一切顺利!

質問

seata不是有两个端口吗 一个是7091一个是8091 前者是控制台的访问端口 后者是服务的访问端口 我想两个端口都保留 怎么修改

思考

Configuring network ports

OK, let me see. I’m piecing together Seata's ports, suggesting server.port=7091 for console access and seata.server.port=8091 for Netty server interaction.

Configuring ports

Separating console and Netty ports involves setting server.port=7091 for console access and locating the correct port for Netty server configuration in Seata's registry configuration.

Confirming configurations

I'm piecing together that setting server.port=7091 keeps the console on port 7091, while the Netty server naturally stays on port 8091 by default.

Ensuring port separation

OK, let me see: setting server.port=7091 keeps the console on port 7091. The Netty server stays on port 8091 by default, so no changes are needed.

Thought about Seata多端口配置,持续 10 秒

回答

Seata Server 中,通常会有 两个端口

  1. Spring Boot 控制台端口(HTTP/管理端口):默认是 server.port,例如 7091。用于访问 Seata 的控制台(即前端界面)以及健康检查等常规 HTTP 请求。
  2. Seata 事务协调器(TC)的 RPC 端口(Netty 监听端口):默认是 8091。负责 Client <-> Server 之间的分布式事务通信。

很多人一开始会误以为只有一个端口,但其实 Seata 默认就会开启 8091 作为 RPC 服务端口;而如果你用的是官方提供的 seata-server(基于 Spring Boot 的),那它还会同时使用 server.port(默认为 7091,或者其他端口)来提供控制台/管理接口。


1. 让控制台和事务端口都保留并分别监听

如果你想 同时保留

  • 控制台端口: 7091
  • Seata 事务端口(Netty RPC): 8091

那么通常只需要:

  1. 在 Spring Boot 的 application.yml(或 application.properties)里指定

    yaml
    server: port: 7091

    这样,访问控制台就用 http://{ip}:7091

  2. 在 Seata 的配置中心(或本地 file.conf)里,配置 service.default.grouplistservice.vgroupMapping.xxx 等时,指向的是 8091

    • Seata Server 启动时,它会默认使用 8091 端口 作为 Netty 服务器的监听端口(RPC 通信端口),无需额外手动配置也行。
    • 如果你想改这个默认 RPC 端口,则需要在相应的 Seata 配置中(例如 file.conf / nacosseataServer.properties)增加显式配置。但 大多数版本默认就是 8091

1.1 核心配置示例

Seata Server 的 application.yml 示例

yaml
server: port: 7091 # 用于控制台/管理的HTTP端口 spring: application: name: seata-server # 其他省略...

Seata Server 在 Nacos 上的 seataServer.properties(或本地 file.conf)

properties
# 指定给客户端使用的RPC地址列表(直连模式才需要),或者只用来做默认值 service.vgroupMapping.default_tx_group=default service.default.grouplist=192.168.28.28:8091 # 其他参数,如 store.db.*、transport.* 等等...

注意:如果你启用了 Nacos 注册中心,那么客户端可能并不会真正使用 service.default.grouplist 中的 8091 直连,而是通过 Nacos 服务发现到 8091。此时更要确保 Seata Server 已正确在 Nacos 中注册,且暴露的是 8091 端口。


2. 如果要显式指定 Seata RPC 端口

某些场景下你想改掉默认的 8091,或者希望在配置文件中写明。Seata(从 1.x 到 1.6+)通常有两种方式:

  1. 老版本(很多教程中看到的做法):在 registry.conf 或者 file.conf 里,通过 serverAddress 来显式指定。
  2. 较新版本:在 service.default.grouplist 或者 service.vgroupMapping.xxx 后面写上 IP:PORT 即可。如果是通过 Nacos/Eureka 等注册中心,则以它们注册的端口信息为准。

目前在 官方最新版本中,大多数情况下只要你不修改 Netty 监听端口,就会默认启用 8091。如果确实要改,你可以在 transport 相关配置块里手动加入(注意版本差异),例如:

properties
transport.server-port=9999

然后再把 service.default.grouplist=127.0.0.1:9999 这种对应上。


3. 客户端如何连上 8091

Client 侧(Spring Boot 工程)只要有下面类似配置,就能连到 8091:

yaml
seata: # 对应 Seata Server 中 service.vgroupMapping.xxx 的 xxx tx-service-group: default_tx_group # 如果不走注册中心,而是直接写死IP:PORT service: vgroup-mapping: default_tx_group: default group-list: default: 192.168.28.28:8091

或是通过 Nacos 注册中心发现 8091,则只需要保证:

  • Seata Server 在 Nacos 中注册成功(并且注册的端口是 8091)
  • Client 的 seata.registry.type=nacos 并配置了同一个 namespace, group, cluster
  • 两者 service.vgroupMapping.xxx 一致

4. 小结

  • Spring Boot 的 server.port=7091:启动 Seata Server 的控制台端口,HTTP 服务跑在 7091。
  • Seata Server 默认 Netty RPC 端口 8091:用于分布式事务通信。
  • 保留两个端口:你只需在 application.yml 里设置 server.port=7091,并且在 Seata 配置中心或本地 service.vgroupMapping.* 配置中指向 8091,即可实现前者访问控制台、后者处理事务。
  • 如果要改 Netty 端口,就在配置文件(Nacos / file.conf)中做相应设置并让客户端对应上。

这样便能同时保留 7091(供浏览器访问控制台)和 8091(供应用进行分布式事务交互)两条通道。

質問

详细帮我输出 分三个配置来

思考

Outlining configurations

I’m preparing to break down the Seata server setup into three sections: server side with application.yaml, NACOS config, and client side with application.yaml, ensuring specific ports are maintained.

Thought about Seata双端口配置,持续 几秒

回答

下面给出 三份配置示例,以便 同时保留

  • Seata 控制台(HTTP 管理)端口7091
  • Seata 事务协调器(Netty RPC)端口8091

其中示例中使用了 Nacos 作为注册中心与配置中心。请根据你自己的 IP、端口、数据库、Nacos namespace 等进行适当修改。


1. Seata Server 的 Spring Boot 配置application.yml

在 Seata 官方的 seata-server 工程里,或者你自己打包的 Seata Server Spring Boot 应用中,修改 application.yml

yaml
server: # 这个端口用于 Seata Server 的控制台(HTTP 管理端口) port: 7091 spring: application: name: seata-server logging: file: path: ${user.home}/logs/seata # 如果有自己的 logback 配置,可以在这儿指定 config: classpath:logback-spring.xml # Seata 核心配置: 让它去读 Nacos 上的 data-id = "seataServer.properties" seata: # 配置中心 config: type: nacos nacos: server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties # 注册中心 registry: type: nacos nacos: application: seata-server server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP cluster: seatatest username: nacos password: nacos # 控制台登录(可选) security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login console: user: username: seata password: seata

说明:

  1. server.port=7091:此端口仅用于 HTTP 控制台访问。
  2. Netty RPC 端口默认是 8091(Seata 内部会自动使用 8091 进行事务协调)。如果想改成别的,比如 9991,可以在后面所示的 seataServer.properties 中进行显式设置。
  3. 如果你只是想保留默认的 8091,就不需要额外写。Seata 启动后会在日志中提示它的监听端口是哪一个。

2. Nacos 配置中心seataServer.properties

在 Nacos 的 Namespace = test-prodGroup = SEATA_GROUP 下,新建或编辑 Data ID = seataServer.properties,写入内容示例:

properties
########## transport ########## 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 # 如果想把 Netty RPC 改成别的端口,而不是 8091,可以加: # transport.server-port=8091 ########## service ########## # tx-service-group 映射。Client 设置 tx-service-group=seatatest 时,对应 seatatest service.vgroupMapping.seatatest=seatatest # 如果是直连模式,这里会生效,告诉客户端要连 8091 service.default.grouplist=192.168.28.28:8091 service.enableDegrade=false service.disableGlobalTransaction=false ########## 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 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k # Undo 日志保留天数/清理周期 server.undo.logSaveDays=7 server.undo.logDeletePeriod=86400000 ########## log ########## log.exceptionRate=100 ########## store(主要是 TC 端使用) ########## store.mode=db store.lock.mode=db store.session.mode=db # 数据源信息(示例:MySQL) store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://192.168.28.28:3305/seata?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false store.db.user=admin store.db.password=NWT@temp123 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.lockTable=lock_table store.db.queryLimit=100 store.db.maxWait=5000 ########## 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 server.enableParallelRequestHandle=false ########## metrics ########## metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898

说明:

  1. 默认情况下,Seata 在 Netty RPC 端会监听 8091。若你想改端口,可以在这里加上 transport.server-port=xxxx
  2. 如果 Client 用的是 Nacos 注册中心,则它会通过 Nacos 的服务发现到 Seata Server 注册的真实 host:port,所以这里的 service.default.grouplist 在注册中心模式下只是一个备用配置。
  3. service.vgroupMapping.seatatest=seatatest 对应客户端的 seata.tx-service-group=seatatest

3. Spring Boot Client 侧配置(示例 application.yml

在你的业务微服务中,如果使用 Spring Boot + Seata:

yaml
spring: application: name: your-biz-service seata: enabled: true # 这里对应 service.vgroupMapping.seatatest=seatatest tx-service-group: seatatest # 是否自动代理数据源,可根据实际情况选择 enable-auto-data-source-proxy: false # 如果启用 Nacos 做注册中心,就和 Server 保持一致 registry: type: nacos nacos: application: seata-server server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP cluster: seatatest username: nacos password: nacos # 配置中心:和 Seata Server 保持一致 config: type: nacos nacos: server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties service: # 如果要强制直连模式,可写 group-list # vgroup-mapping: # seatatest: default # group-list: # default: 192.168.28.28:8091 disable-global-transaction: false

说明:

  1. tx-service-group: seatatest 一定要与 服务端配置中的 service.vgroupMapping.seatatest=... 保持一致,否则会报 “No available service” 等错误。
  2. 如果确实要直连,你可以在上面 service 块里把 group-list 的 IP:PORT 设成 SeataServerIP:8091;但前提是 Server 端默认监听 8091,且能通。
  3. 如果使用 Nacos 注册中心,那么只要 Server 成功注册到 Nacos(application = seata-server,cluster = seatatest,端口 8091),Client 就能正常发现并连上。

4. 如何验证是否成功

  1. 访问控制台:启动 Seata Server 后,在浏览器打开 http://{你的主机IP}:7091。如果能看到 Seata 的登录页,说明 控制台端口 7091 正常。
  2. 查看日志:Seata Server 启动日志里会提示 “Server started, port: 8091” 或类似信息,表示 RPC 通信端口 正常。
  3. Nacos 注册情况:如果你配置了注册中心,去 Nacos 控制台看看有没有一个服务名为 seata-server、集群 seatatest,下方有实例暴露 8091 端口。
  4. Client 能否成功发起全局事务:看 Client 侧是否能与 Seata Server 通讯成功,不会再报 “No available service” 之类的错误。

这样就能 同时保留 7091(Seata 控制台) 和 8091(RPC 端口) 这两个端口,互不冲突,各司其职。祝使用顺利!

質問

2024-12-31 14:50:11.236 INFO 32368 --- [_RMROLE_1_38_40] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=10.23.24.6:8091:3576461000735551497,branchId=3576461000735551502,branchType=AT,resourceId=jdbc:mysql://192.168.28.33:3306/owm,applicationData=null
2024-12-31 14:50:11.236 INFO 32368 --- [_RMROLE_1_38_40] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 10.23.24.6:8091:3576461000735551497 3576461000735551502 jdbc:mysql://192.168.28.33:3306/owm
2024-12-31 14:50:11.236 INFO 32368 --- [_RMROLE_1_38_40] i.seata.rm.datasource.DataSourceManager : branchRollback failed. branchType:[AT], xid:[10.23.24.6:8091:3576461000735551497], branchId:[3576461000735551502], resourceId:[jdbc:mysql://192.168.28.33:3306/owm], applicationData:[null]. reason:[Branch session rollback failed and try again later xid = 10.23.24.6:8091:3576461000735551497 branchId = 3576461000735551502 dynamic-datasource can not find primary datasource]
2024-12-31 14:50:11.237 INFO 32368 --- [_RMROLE_1_38_40] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable

思考

Analyzing rollback failure

I’m digging into a rollback error in Seata, highlighting potential issues with dynamic data source configuration, consistency with Seata setups, and primary data source recognition.

Struggling with dynamic data sources

I'm looking at issues with dynamic data sources in Seata, possibly from plugins like dynamic-datasource-spring-boot-starter. Accurate Seata data source configuration and matching names are crucial.

Ensuring seamless integration

I'm pulling together ways to integrate Seata with dynamic data sources, including specifying data source names, enabling proxies for AT mode, and checking configurations to prevent rollback failures.

Thought about Seata branch rollback error,持续 12 秒

回答

从日志上看,Seata 在做分支回滚时,出现了:

text
branchRollback failed... reason:[Branch session rollback failed and try again later xid = 10.23.24.6:8091:3576461000735551497 branchId = 3576461000735551502 dynamic-datasource can not find primary datasource]

这说明 Seata 要回滚该分支事务时,调用了 分布式事务资源管理器(RM),但 动态数据源(dynamic-datasource) 无法匹配到对应的数据源,导致回滚失败,并返回了 “PhaseTwo_RollbackFailed_Retryable”。(Seata 会尝试再次进行回滚,一直重试。)

在使用 AT 模式 时,Seata 需要感知并代理你的实际数据源,以便在需要回滚时执行对应的 Undo SQL。然而现在它提示找不到对应的 datasource,通常是因为:

  1. 动态数据源的 “primary” 未配置 或者与 Seata 侧感知到的 resourceId 不一致。
  2. Seata dataSourceProxy 未生效,即代码里没有将真正的数据源托管给 Seata,而是被其他动态数据源管理器接管。
  3. resourceId(形如 jdbc:mysql://host:port/dbname) 与实际 dataSource 名、URL 不匹配,导致 Seata 在回滚时无法定位正确数据库连接。
  4. 你使用了第三方的动态数据源框架(例如 baomidoudynamic-datasource-spring-boot-starter),但并未做与 Seata 的正确联动配置。

下面给出一些常见的排查 / 解决思路。


1. 如果使用了「Baomidou Dynamic Datasource」等第三方动态数据源

1.1 指定默认数据源(primary)

  • application.yml 中,明确指定默认数据源:

    yaml
    spring: datasource: dynamic: primary: owm # 这就是你想指定为默认的数据源名称 datasource: owm: url: jdbc:mysql://192.168.28.33:3306/owm?useSSL=false&useUnicode=true... username: xxx password: xxx driver-class-name: com.mysql.cj.jdbc.Driver

    如果 Seata 在回滚时没有找到 “owm”,就会报「can not find primary datasource」。所以需要确保你已经设置了 primary 的名字,并且它对应的 URL 正好是 jdbc:mysql://192.168.28.33:3306/owm

  • 如果你有多个动态数据源,需要确认在执行分支事务时,实际使用的是哪个数据源?Seata 回滚时就要找那个数据源,名字要和 Seata resourceId 对上。

1.2 与 Seata 集成

  • 方式一:通过 Seata 原生的 自动代理
    application.yml 里加上:

    yaml
    seata: enable-auto-data-source-proxy: true # 其他Seata的配置...

    这样 Seata 会扫描 Spring 容器里的数据源并自动代理。但是当使用了 Dynamic Datasource 时,有时候会和其原生的 Bean 初始化顺序冲突,需要仔细调试。

  • 方式二:显式通过 Baomidou 侧提供的 Seata 拦截器 / 代理适配器。
    Baomidou 在 3.x 之后支持简单的 Seata 适配,如果你用的是最新版(比如 3.5+),可以在官方文档中查一下「[Dynamic Datasource - Seata 集成]」。有些场景下需要额外加注解或配置。

1.3 resourceId 匹配问题

  • Seata 默认在 AT 模式下,会把 jdbc:mysql://IP:Port/DBName 当作 resourceId。如果你的实际数据源在 Baomidou 里配置的是类似 xxx?useUnicode=true&characterEncoding=utf8,那么 Seata 识别时,也需要确保能匹配到同一个字符串(或者至少是同一个数据库地址)。
  • 如果实在匹配不上,可以在 MyBatis 或 DataSource 初始化时,通过一些 Seata 自定义参数(例如 seata.datasource.resourceName)来显式指定 resourceId,但在大多数场景下只要 URL 一致就行。

2. 如果并未主动使用第三方动态数据源

那就需要检查:

  1. 是否有类似 spring.datasource.url=jdbc:mysql://192.168.28.33:3306/owm 配置,但在 Seata 回滚时,却拿到 jdbc:mysql://192.168.28.33:3306/owm 而无法匹配到 DataSource。
  2. 你的项目里是否引入了其他多数据源相关的依赖?导致 Spring 中出现了意料之外的 DataSource Bean 名字冲突或初始化顺序问题。
  3. Seata 的自动代理是否被关闭了?检查 seata.enable-auto-data-source-proxyspring.cloud.alibaba.seata.tx-service-group 之类的配置。

3. 常见排查步骤

  1. 确认 Undo Log 表:在使用 AT 模式时,需要在所操作的数据库 owm 里建一个 undo_log 表,Seata 执行回滚时会去这个表里找对应的记录。如果这个表不存在,也会导致回滚失败(不过错误提示会不一样)。
  2. 确认 DataSource Bean:在你的业务项目中,找一下最终生效的 DataSource 是不是只有一个?名字叫啥?是否带有 “primary = true”?
  3. 打印 Seata DEBUG 日志:把 Seata 或 Baomidou dynamic-datasource 的日志级别设置为 DEBUG,看看在启动时是如何加载数据源、如何被 Seata 代理的。
  4. 与本地测试对比:本地只搞一个最简单的单数据源 + Seata AT,对照看能否成功回滚。若本地可以,线上不行,多半就是线上多数据源 / 配置顺序导致的问题。

4. 解决思路示例

假设你用 Baomidou dynamic-datasource 并且只有一个数据源,名叫 owm,可以尝试这么写:

4.1 application.yml 里的数据源配置

yaml
spring: datasource: dynamic: primary: owm # 指定默认数据源名称 datasource: owm: url: jdbc:mysql://192.168.28.33:3306/owm?useSSL=false&characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

4.2 开启 Seata 自动代理

yaml
seata: enabled: true enable-auto-data-source-proxy: true application-id: your-app tx-service-group: seatatest # 需要与 server 那边service.vgroupMapping.seatatest对应 registry: type: nacos nacos: server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos cluster: seatatest application: seata-server config: type: nacos nacos: server-addr: 192.168.28.32:8848 namespace: test-prod group: SEATA_GROUP username: nacos password: nacos data-id: seataServer.properties service: disable-global-transaction: false

4.3 确保 resourceId 匹配

  • 只要 Baomidou 在内部实际创建的 JDBC URL 是 jdbc:mysql://192.168.28.33:3306/owm,Seata 默认就能识别到同一个 resourceId。
  • 如果你带了很多参数,比如 jdbc:mysql://192.168.28.33:3306/owm?useUnicode=true&characterEncoding=utf-8...,一般 Seata 也可以识别,只要 IP、端口、库名一致就行。
  • 若要强制指定 resourceId,可以通过 Seata 的 DataSourceProxy 里的一些自定义配置,但大部分情况下不需要。

5. 总结

  • 核心报错dynamic-datasource can not find primary datasource,说明 动态数据源没能匹配到当前要回滚的数据库连接。
  • 优先检查
    1. 是否有给 dynamic-datasource 指定 primary
    2. Seata 代理后的 URL 与 dynamic-datasource 配置的 URL 名称是否一致;
    3. 是否开启了 Seata dataSourceProxy;
    4. 多数据源场景中是否只有一部分数据源被 Seata 接管,另一些没有接管,导致回滚时找不到相应。
  • 解决:在项目中正确配置默认数据源,或者给每个数据源都配置 Seata 代理(若多库事务),并确保 Seata 与 dynamic-datasource 的配置兼容。

只要让 Seata 知道「哪一个数据源对应 jdbc:mysql://192.168.28.33:3306/owm」并成功执行 Undo/Redo,就能正常完成回滚操作。祝调试顺利!

このQ&Aを共有