赞
踩
在分布式系统中,事务管理是一个非常重要的问题。Seata是一款轻量级的分布式事务解决方案,它基于阿里巴巴的Dubbo RPC框架,并且可以与Spring Cloud集成。本文将介绍如何在Spring Cloud Alibaba中使用Seata,包括Seata的架构、配置以及使用方法。
准备工作
在开始配置Spring Cloud Alibaba 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与Nacos进行融合,可以按照以下步骤操作:
从Seata官网仓库下载源码包,并解压,建表。
Seata Server 共有以下 3 种存储模式(store.mode):
模式 | 说明 | 准备工作 |
---|---|---|
file | 文件存储模式,默认存储模式; 该模式为单机模式,全局事务的会话信息在内存中读写,并持久化本地文件 root.data,性能较高 | - |
db | 数据库存储模式; 该模式为高可用模式,全局事务会话信息通过数据库共享,性能较低。 | 建数据库表 |
redis | 缓存处处模式; Seata Server 1.3 及以上版本支持该模式,性能较高,但存在事务信息丢失风险, | 配置 redis 持久化配置 |
在 db 模式下,我们需要针对全局事务的会话信息创建以下 3 张数据库表。
在 MySQL 中,创建一个名为 seata 的数据库实例,并在该数据库内执行以下 SQL。
global_table 的建表 SQL 如下。
- less复制代码-- -------------------------------- 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 如下。
- less复制代码-- 事务分支表 --
- 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 如下。
- less复制代码-- 锁定表--
- 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 如下。
- 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必须在每个业务数据库中出现,用于保存回滚操作数据。
Seata的UNDO_LOG表和数据库的UNDO_LOG是相似的,只不过它们的范围不一样
undo_log 的建表 SQL 如下。
- r复制代码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;
注意:
在Seata的script/config-center路径下获取config.txt文件。
- ini复制代码#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的配置,具体包括:
seataServer.properties
配置。default
和registry.conf
中的default
。启动Seata和Nacos服务,完成Seata与Nacos的融合。
以上步骤详细描述了将Seata与Nacos进行融合的过程。请根据实际需求进行相应的调整和修改。
在项目中添加如下依赖:
- xml复制代码<dependency>
- <groupId>io.seata</groupId>
- <artifactId>seata-all</artifactId>
- <version>1.7.0</version>
- </dependency>
在项目的配置文件中 application.yml 添加如下配置:
- yaml复制代码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配置中心集群。在Spring Cloud Alibaba中使用Seata非常简单,只需要按照如下步骤进行操作:
首先需要启动Seata Server,Seata Server是一个独立的进程,用于管理全局事务。可以通过下载Seata Server并运行启动脚本来启动Seata Server。
在使用Seata时,需要使用数据源代理来拦截SQL语句,从而实现分支事务的管理。在Spring Cloud Alibaba中,可以使用Seata提供的数据源代理来实现。
具体来说,可以在项目中引入Seata提供的数据源代理依赖,并在数据源配置中使用Seata提供的数据源代理类来代理数据源。
例如,在使用Druid作为数据源时,可以按照如下方式配置数据源代理:
- xml复制代码<dependency>
- <groupId>io.seata</groupId>
- <artifactId>seata-datasource-proxy</artifactId>
- <version>1.7.0</version>
- </dependency>
- yaml复制代码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注解:
- less复制代码@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()方法,从而实现了分布式事务。
如果在配置或使用过程中遇到了问题,可以检查以下几点:
通过仔细检查这些关键点,你应该能够顺利地配置和使用Spring Cloud Alibaba Seata,实现高效的数据存储和处理。
本文介绍了在Spring Cloud Alibaba中使用Seata的方法,包括Seata的架构、配置以及使用方法。通过使用Seata,可以在分布式系统中实现分布式事务的管理,从而保证数据的一致性和完整性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。