Seata➕Nacos配置
前言
在分布式系统中,事务管理是一个非常重要的问题。Seata是一款轻量级的分布式事务解决方案,它基于阿里巴巴的Dubbo RPC框架,并且可以与Spring Cloud集成。本文将介绍如何在Spring Cloud Alibaba中使用Seata,包括Seata的架构、配置以及使用方法。
准备工作
在开始配置Spring Cloud Alibaba Seata之前,我们需要先确保系统已满足以下条件:
- Java运行环境已安装
- Maven已安装
- 了解Spring Boot基础知识
Seata架构
Seata的架构包括三个组件:Transaction Coordinator(TC)、Transaction Manager(TM)和Resource Manager(RM)。其中,TC负责全局事务的协调和管理,TM负责本地事务的管理,RM负责资源的分支事务管理。
下图展示了Seata的基本架构:
在Seata中,全局事务由一个TC实例进行协调和管理。TC可以通过与TM和RM进行通信来实现全局事务的提交和回滚。
在一个分布式系统中,每个服务都可以作为一个TM实例运行。当服务需要进行事务操作时,TM会向TC注册一个本地事务,并且向RM发送分支事务请求。RM可以将分支事务与本地事务进行关联,从而实现分支事务的管理。
Seata融合Nacos
Seata远端配置
Seata是一款开源的分布式事务解决方案,而Nacos是一款分布式配置中心。要将Seata与Nacos进行融合,可以按照以下步骤操作:
从Seata官网仓库下载源码包,并解压,建表。
Seata Server 共有以下 3 种存储模式(store.mode):
模式 | 说明 | 准备工作 |
---|---|---|
file | 文件存储模式,默认存储模式; 该模式为单机模式,全局事务的会话信息在内存中读写,并持久化本地文件 root.data,性能较高 | - |
db | 数据库存储模式; 该模式为高可用模式,全局事务会话信息通过数据库共享,性能较低。 | 建数据库表 |
redis | 缓存处处模式; Seata Server 1.3 及以上版本支持该模式,性能较高,但存在事务信息丢失风险, | 配置 redis 持久化配置 |
在 db 模式下,我们需要针对全局事务的会话信息创建以下 3 张数据库表。
- 全局事务表,对应的表为:global_table
- 分支事务表,对应的表为:branch_table
- 全局锁表,对应的表为:lock_table
在 MySQL 中,创建一个名为 seata 的数据库实例,并在该数据库内执行以下 SQL。
global_table 的建表 SQL 如下。
-- -------------------------------- storeMode为“db”时使用的脚本 --------------------------------
-- 全局事务表--
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;
branch_table 的建表 SQL 如下。
-- 事务分支表 --
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;
lock_table 的建表 SQL 如下。
-- 锁定表--
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`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;
distributed_lock 的建表 SQL 如下。
-- seata新版本加的锁表--
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
PRIMARY KEY (`lock_key`)
) ENGINE = INNODB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
数据库中添加回滚日志表UNDO_LOG:
UNDO_LOG必须在每个业务数据库中出现,用于保存回滚操作数据。
- 当全局事务提交时,对应的UNDO_LOG记录直接删除。
- 当全局事务回滚时,通过该表回滚到以前的数据,并删除UNDO_LOG记录。
Seata的UNDO_LOG表和数据库的UNDO_LOG是相似的,只不过它们的范围不一样
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,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
注意:
- 前面的UNDO_LOG表是加在业务数据库中的。每个业务数据库都要有UNDO_LOG
- seata1.4.2之后,需要回滚的表日期类型不能使用datetime,可以使用timestamp
在Seata的script/config-center
路径下获取config.txt
文件。
#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
#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=file
store.lock.mode=file
store.session.mode=file
#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
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=username
store.db.password=password
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
#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100
#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
在Nacos配置中心里新建seataServer.properties
配置,并将config.txt
文件内的内容复制进去。
修改Seata服务的配置,具体包括:
-
获取Seata的程序包并解压。
-
进入
conf
目录下的registry.conf
文件。 -
找到
registry
下的type
配置,将其改为nacos
。 -
找到
nacos
配置并进行修改。config { type = "nacos" nacos { serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "" username = "nacos" password = "nacos" } } registry { type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } }
编辑Nacos的配置,具体包括:
- 在Nacos配置中心里新建
seataServer.properties
配置。 - 将Seata的配置信息复制进去,如
default
和registry.conf
中的default
。
启动Seata和Nacos服务,完成Seata与Nacos的融合。
以上步骤详细描述了将Seata与Nacos进行融合的过程。请根据实际需求进行相应的调整和修改。
Seata本地配置
在Spring Cloud Alibaba中使用Seata需要进行如下配置:
- 添加依赖
在项目中添加如下依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.7.0</version>
</dependency>
- 配置文件
在项目的配置文件中 application.yml
添加如下配置:
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: ${spring.application.name}-fescar-service-group
service:
vgroup-mapping:
${spring.application.name}-fescar-service-group: default
group-default:
default:
defaultGlobalTransactionTimeout: 60000
registry:
type: nacos
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group: SEATA_GROUP
cluster: default
config:
type: nacos
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group: SEATA_GROUP
cluster: default
seata.enabled
:是否启用Seata。seata.application-id
:Seata应用ID,通常使用Spring应用程序名称。seata.tx-service-group
:Seata事务服务组ID,通常使用${spring.application.name}-fescar-service-group
。seata.service.vgroup-mapping
:Seata服务分组映射。seata.service.group-default
:Seata服务默认配置。seata.registry.type
:Seata注册中心类型,这里使用的是Nacos。seata.registry.nacos.server-addr
:Nacos注册中心地址,从Spring Cloud配置中心获取。seata.registry.nacos.namespace
:Nacos注册中心命名空间,从Spring Cloud配置中心获取。seata.registry.nacos.group
:Nacos注册中心分组。seata.registry.nacos.cluster
:Nacos注册中心集群。seata.config.type
:Seata配置中心类型,这里使用的是Nacos。seata.config.nacos.server-addr
:Nacos配置中心地址,从Spring Cloud配置中心获取。seata.config.nacos.namespace
:Nacos配置中心命名空间,从Spring Cloud配置中心获取。seata.config.nacos.group
:Nacos配置中心分组。seata.config.nacos.cluster
:Nacos配置中心集群。
Seata使用方法
在Spring Cloud Alibaba中使用Seata非常简单,只需要按照如下步骤进行操作:
- 启动Seata Server
首先需要启动Seata Server,Seata Server是一个独立的进程,用于管理全局事务。可以通过下载Seata Server并运行启动脚本来启动Seata Server。
- 配置数据源代理
在使用Seata时,需要使用数据源代理来拦截SQL语句,从而实现分支事务的管理。在Spring Cloud Alibaba中,可以使用Seata提供的数据源代理来实现。
具体来说,可以在项目中引入Seata提供的数据源代理依赖,并在数据源配置中使用Seata提供的数据源代理类来代理数据源。
例如,在使用Druid作为数据源时,可以按照如下方式配置数据源代理:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-datasource-proxy</artifactId>
<version>1.7.0</version>
</dependency>
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
proxy-filters: com.alibaba.druid.filter.stat.StatFilter, io.seata.rm.datasource.SeataDataSourceProxy
在数据源配置中,将Seata提供的数据源代理类io.seata.rm.datasource.SeataDataSourceProxy
添加到proxy-filters
中即可。
- 配置分布式事务
主启动上加注解 @EnableTransactionManagement
在进行分布式事务操作时,需要通过Seata提供的@GlobalTransactional
注解来标记全局事务。
例如,在使用MyBatis进行数据库操作时,可以按照如下方式使用@GlobalTransactional
注解:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@GlobalTransactional
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
User fromUser = userMapper.selectByAccount(fromAccount);
User toUser = userMapper.selectByAccount(toAccount);
fromUser.setBalance(fromUser.getBalance().subtract(amount));
toUser.setBalance(toUser.getBalance().add(amount));
userMapper.updateByPrimaryKey(fromUser);
userMapper.updateByPrimaryKey(toUser);
}
}
在上述代码中,使用@GlobalTransactional
注解标记了transfer()
方法,从而实现了分布式事务。
故障排除
如果在配置或使用过程中遇到了问题,可以检查以下几点:
- Seata相关配置是否正确
- Seata Server是否正常启动,并可以被访问
- Seata Client是否正确配置,并与Seata Server保持连接
- 业务代码中是否正确使用了Seata的事务处理机制
通过仔细检查这些关键点,你应该能够顺利地配置和使用Spring Cloud Alibaba Seata,实现高效的数据存储和处理。
小结
本文介绍了在Spring Cloud Alibaba中使用Seata的方法,包括Seata的架构、配置以及使用方法。通过使用Seata,可以在分布式系统中实现分布式事务的管理,从而保证数据的一致性和完整性。
标签:Seata,lock,配置,Nacos,server,client,store,seata From: https://blog.51cto.com/u_15399050/7543410