1 以Nacos作为seata配置中心快速集成
首先需要搭建好Nacos服务,本实例使用AT模式,相关核心依赖版本号如下:
- seata 1.7.0
- seata-spring-boot-starter 1.7.0
- spring-cloud-starter-alibaba-nacos-discovery 2022.0.0.0
- springboot 3.0.2
- spring-cloud-starter 2022.0.0
1.1 创建Seata Server Database (TC)
创建seata
数据库,并执行以下配置脚本:
-- -------------------------------- 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_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- 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 = utf8mb4;
-- 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),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
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);
1.2 下载 Seata
点击下载 Seata 1.7.0 版本
1.3 同步配置到Nacos
新建 seata-config
命名空间,然后把参数同步到配置中心,具体操作如下:
-
解压下载的
seata-server-1.7.0.zip
,然后进入config-center
目录,把config.text文件往前面移动一个目录到script
目录里面,并修改config.text,数据库连接别忘记修改,大体内容如下:#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=db store.lock.mode=db store.session.mode=db #Used for password encryption store.publicKey= #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 #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
-
然后执行以下命令命令:
sh yourSeataPath/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u username -w password
-
脚本示例:
sh /Users/xxx/Downloads/seata/script/config-center/nacos/nacos-config.sh -h local-nacos.xxx.com -p 8848 -g SEATA_GROUP -t seata-config -u admin -w xxx
参数说明:
-
-h: host,默认值 localhost
-
-p: port,默认值 8848
-
-g: 配置分组,默认值为 ‘SEATA_GROUP’
-
-t: 租户信息,对应 Nacos 的命名空间 ID 字段, 默认值为空 ‘’
注:该步骤,一个nacos地址,仅需要配置一次即可
1.4 配置 Seata-server
修改 seata-server-1.7.0\conf\application.yml
配置文件中的配置项如下:
server:
port: 6688
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@666888.
seata:
# nacos配置
config:
type: nacos
nacos:
server-addr: xxx
namespace: seata-config
group: SEATA_GROUP
username: xxx
password: xxx
context-path:
data-id: seataServer.properties
registry:
# nacos配置
type: nacos
nacos:
application: seata-server
server-addr: xxx
namespace: seata-config
group: SEATA_GROUP
# tc集群名称
cluster: default
username: xxx
password: xxx
server:
#If not configured, the default is '${server.port} + 1000'
service-port: 7688
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017666888
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
1.5 启动 Seata-server
Enter the start directory: cd /smilehappiness-framework-base/smilehappiness-seata/src/main/java/start
Execution: ./seata-server.sh
Note: If you do not have permission, you can add executable permission (chmod+x./seata server. sh)
1.6 Seata 控制台访问
Seata 1.5.1 开始支持控制台 本地访问控制台地址:http://127.0.0.1:6688,通过 Seata
内置的控制台可以观察正在执行的事务信息和全局锁信息,事务执行结束即删除相关信息。
2 使用Seata 示例
2.1 添加依赖
<!--seata client config-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--SpringCloud users can introduce spring cloud starter libaba seata, which XID has already implemented cross service delivery internally-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
</dependency>
2.2 配置nacos,在seata-config命名空间里面,新增分组为 SEATA_GROUP, dataId 如下:
service.vgroupMapping.seata-example-tx-group=default
2.3 创建seata client database
Seata AT模式需要使用到undo_log
表,所以在需要使用seata的相关的服务里面,都需要添加以下表:
CREATE TABLE `undo_log`
(
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(128) NOT NULL COMMENT 'global transaction id',
`context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` longblob NOT NULL COMMENT 'rollback info',
`log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` datetime(6) NOT NULL COMMENT 'create datetime',
`log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
2.4 创建测试用例中业务所需要的数据库表
CREATE DATABASE IF NOT EXISTS seata_order;
CREATE DATABASE IF NOT EXISTS seata_account;
CREATE DATABASE IF NOT EXISTS seata_product;
DROP TABLE IF EXISTS seata_account.account;
CREATE TABLE seata_account.account
(
id INT(11) NOT NULL AUTO_INCREMENT,
balance DOUBLE DEFAULT NULL,
last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
insert into seata_account.account (id, balance)
VALUES (1, 50);
DROP TABLE IF EXISTS seata_order.p_order;
CREATE TABLE seata_order.p_order
(
id INT(11) NOT NULL AUTO_INCREMENT,
user_id INT(11) DEFAULT NULL,
product_id INT(11) DEFAULT NULL,
amount INT(11) DEFAULT NULL,
total_price DOUBLE DEFAULT NULL,
status VARCHAR(100) DEFAULT NULL,
add_time DATETIME DEFAULT CURRENT_TIMESTAMP,
last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
DROP TABLE IF EXISTS seata_product.product;
CREATE TABLE seata_product.product
(
id INT(11) NOT NULL AUTO_INCREMENT,
price DOUBLE DEFAULT NULL,
stock INT(11) DEFAULT NULL,
last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
insert into seata_product.product (id, price, stock)
VALUES (1, 10, 20);
2.5 配置seata client nacos配置
在需要集成seata的项目中,需要在application.yml中配置seata相关配置:
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: seata-example-tx-group
#it must be false, otherwise the component will automatically use a DataSourceProxy to wrap the DataSource when seata is enabled internally
enable-auto-data-source-proxy: false
service:
# https://seata.apache.org/zh-cn/docs/user/configurations/
# 更多 Seata-server以Nacos作为配置中心的配置请参考:https://seata.io/zh-cn/docs/ops/deploy-by-docker-compose/#nacos-db
vgroupMapping:
# Transaction routing rules configuration, only for the client,The key corresponds to the value of tx service group above
# Specify the transaction grouping to cluster mapping relationship (the cluster name on the right side of the equal sign needs to be consistent with the cluster registered by Seata Server)
seata-example-tx-group: default
config:
type: nacos
nacos:
serverAddr: xxx
namespace: seata-config
group: SEATA_GROUP
dataId: seataServer.properties
username: admin
password: xxx
registry:
type: nacos
nacos:
application: seata-server
server-addr: xxx
namespace: seata-config
group: SEATA_GROUP
username: admin
password: xxx
2.6 添加事务注解
重点来了,第一个开启事务的需要添加seata全局事务注解 @GlobalTransactional
,以及@Transactional
,被调用方需要添加事务注解 @Transactional(propagation = Propagation.REQUIRES_NEW)
如smilehappiness-seata-example示例所示
启动客户端服务,项目中出现以下信息,说明client服务启动完成:
register TM success. client version:1.7.0, server version:1.7.0,channel:[id: 0x58c999ba, L:/192.168.80.32:64869 - R:/192.168.80.32:7688]
register RM success. client version:1.7.0, server version:1.7.0,channel:[id: 0xf4b71250, L:/192.168.80.32:65047 - R:/192.168.80.32:7688]
2.7 示例说明
模拟用户下单,扣商品库存,扣用户余额,初步可分为订单服务+商品服务+用户服务。项目中使用了多数据源模拟,实际业务中,多半是微服务实现的,这里测试效果是一样的。
-
模拟正常下单
http://localhost:6687/order/placeOrder { "userId": 1, "productId": 1, "num": 1 }
-
模拟库存不足事务回滚
http://localhost:6687/order/placeOrderProductFail { "userId": 1, "productId": 1, "num": 22 }
-
模拟账户余额不足事务回滚
http://localhost:6687/order/placeOrderAccountFail { "userId": 1, "productId": 1, "num": 6 }
写博客是为了记住自己容易忘记的东西,另外也是对自己工作的总结,希望尽自己的努力,做到更好,大家一起努力进步!
如果有什么问题,欢迎大家一起探讨,代码如有问题,欢迎各位大神指正!
给自己的梦想添加一双翅膀,让它可以在天空中自由自在的飞翔!