一、简介
- 本文主要介绍分布式事务框架seata(seata版本1.4.0)的安装以及springboot与seata的整合的事务回滚案例;
- seata的官方文档路径: http://seata.io/zh-cn/
- 本文介绍的方式是AT模式,采用的中间件是nacos,存储类型为db模式;
- 本文默认你已会nacos的安装,如果不会请移步nacos的官方文档进行搭建;
二、seata服务端安装
2.1 seata-server服务端
2.1.1 下载seata-server服务端
- 本文介绍的是window版本的安装;
- 当前最新版本为1.4.0,选择binary类型的点击下载即可;
- 下载路径: http://seata.io/zh-cn/blog/download.html ;
- 下载下来进行解压,目录结构如下:
2.1.2 添加log文件
- 在根目录创建logs文件夹,里面创建一个seata_gc.log的文件,结构如下:
2.2 初始化数据库
2.2.1 seata库数据表
-
数据库名称就叫seata;
-
branch_table表
CREATE TABLE branch_table ( branch_id bigint(20) NOT NULL, xid varchar(128) NOT NULL, transaction_id bigint(20) DEFAULT NULL, resource_group_id varchar(32) DEFAULT NULL, resource_id varchar(256) DEFAULT NULL, branch_type varchar(8) DEFAULT NULL, status tinyint(4) DEFAULT NULL, client_id varchar(64) DEFAULT NULL, application_data varchar(2000) DEFAULT NULL, gmt_create datetime(6) DEFAULT NULL, gmt_modified datetime(6) DEFAULT NULL, PRIMARY KEY (branch_id), KEY idx_xid (xid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
global_table表
CREATE TABLE global_table ( xid varchar(128) NOT NULL, transaction_id bigint(20) DEFAULT NULL, status tinyint(4) NOT NULL, application_id varchar(32) DEFAULT NULL, transaction_service_group varchar(32) DEFAULT NULL, transaction_name varchar(128) DEFAULT NULL, timeout int(11) DEFAULT NULL, begin_time bigint(20) DEFAULT NULL, application_data varchar(2000) DEFAULT NULL, gmt_create datetime DEFAULT NULL, gmt_modified datetime DEFAULT NULL, PRIMARY KEY (xid), KEY idx_gmt_modified_status (gmt_modified,status), KEY idx_transaction_id (transaction_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
lock_table表
CREATE TABLE lock_table ( row_key varchar(128) NOT NULL, xid varchar(96) DEFAULT NULL, transaction_id bigint(20) DEFAULT NULL, branch_id bigint(20) NOT NULL, resource_id varchar(256) DEFAULT NULL, table_name varchar(32) DEFAULT NULL, pk varchar(36) DEFAULT NULL, gmt_create datetime DEFAULT NULL, gmt_modified datetime DEFAULT NULL, PRIMARY KEY (row_key), KEY idx_branch_id (branch_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.2.2 业务库表
- seata需要在每个业务库添加一张undo_log表
- undo_log表
CREATE TABLE undo_log ( branch_id bigint(20) NOT NULL COMMENT 'branch transaction id', xid varchar(100) 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=utf8 COMMENT='AT transaction mode undo table';
2.3 修改file.conf文件
2.3.1 信息
- 只需要修改模式为db和数据库链接信息即可,如下图:
2.3.2 修改内容
- store.mode = db;
- store.db.url = jdbc:mysql://ip:port/seata;
- store.db.user = 账号
- store.db.password = 密码
2.4 修改registry.conf文件
2.4.1 信息
- 只需要修改注册类型和配置类型均为nacos和nacos的相关信息即可
2.4.2 修改内容
-
registry.type = nacos
-
registry.nacos.application = seata-server, 默认服务端的名称
-
registry.nacos.serverAddr = nacos服务器的8848端口地址
-
registry.nacos.group = SEATA_GROUP, 默认seata的组
-
registry.nacos.namespace = seata, 默认seata的配置在nacos的命名空间
-
registry.nacos.cluster = default, 采取默认集群即可
-
registry.nacos.username = nacos的用户名
-
registry.nacos.password = naocs的密码
-
config.type = nacos
-
config.nacos.namespace = seata, 默认seata的配置在nacos的命名空间
-
config.nacos.group = SEATA_GROUP, 默认seata的组
-
config.nacos.username = nacos的用户名
-
config.nacos.password = naocs的密码
2.5 修改context.txt文件
2.5.1 context.txt文件内容
- 在/seata根目录下创建config.txt文件,内容如下:
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.my_test_tx_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.tableMetaCheckerInterval=60000
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.publicKey=
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.jdbc.Driver
store.db.url=jdbc:mysql://IP:poert/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
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.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
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.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
2.5.2 修改内容
- service.vgroupMapping.my_test_tx_group=default
- store.mode=db
- store.db.url=jdbc:mysql://IP:PORT/seata?useUnicode=true&rewriteBatchedStatements=true
- store.db.user=root
- store.db.password=123456
2.5.3 添加nacos-config.sh文件
- 在seata/conf目录下添加nacos-config.sh文件,内容如下:
#!/bin/sh # Copyright 1999-2019 Seata.io Group. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at、 # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. while getopts ":h:p:g:t:u:w:" opt do case $opt in h) host=$OPTARG ;; p) port=$OPTARG ;; g) group=$OPTARG ;; t) tenant=$OPTARG ;; u) username=$OPTARG ;; w) password=$OPTARG ;; ?) echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1 ;; esac done if [ -z ${host} ]; then host=localhost fi if [ -z ${port} ]; then port=8848 fi if [ -z ${group} ]; then group="SEATA_GROUP" fi if [ -z ${tenant} ]; then tenant="" fi if [ -z ${username} ]; then username="" fi if [ -z ${password} ]; then password="" fi nacosAddr=$host:$port contentType="content-type:application/json;charset=UTF-8" echo "set nacosAddr=$nacosAddr" echo "set group=$group" urlencode() { length="${#1}" i=0 while [ $length -gt $i ]; do char="${1:$i:1}" case $char in [a-zA-Z0-9.~_-]) printf $char ;; *) printf '%%%02X' "'$char" ;; esac i=`expr $i + 1` done } failCount=0 tempLog=$(mktemp -u) function addConfig() { dataId=`urlencode $1` content=`urlencode $2` curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$dataId&group=$group&content=$content&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [ -z $(cat "${tempLog}") ]; then echo " Please check the cluster status. " exit 1 fi if [ "$(cat "${tempLog}")" == "true" ]; then echo "Set $1=$2 successfully " else echo "Set $1=$2 failure " failCount=`expr $failCount + 1` fi } count=0 for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do count=`expr $count + 1` key=${line%%=*} value=${line#*=} addConfig "${key}" "${value}" done echo "=========================================================================" echo " Complete initialization parameters, total-count:$count , failure-count:$failCount " echo "=========================================================================" if [ ${failCount} -eq 0 ]; then echo " Init nacos config finished, please start seata-server. " else echo " init nacos config fail. " fi
2.5.4 将context.txt文件内容上传到nacos
-
目录结构如下:
-
在conf文件下执行命令上传配置文件到nacos
-
此时可以看到nacos上有对应的配置文件信息
2.6 启动服务端seata-server
2.6.1 脚本启动
-
执行/seata/bin/seata-server.bat命令,看到如下日志即是启动成功;
-
同时可以看到nacos上已经注册成功
四、代码整合
4.1 POM依赖
- seata依赖
<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.0</version> </dependency>
4.2 配置文件
- bootstrap.yml内容如下:
seata: enabled: true application-id: ${spring.application.name} enable-auto-data-source-proxy: true #是否开启数据源自动代理 tx-service-group: my_test_tx_group #需要和config.txt上传到nacos上的service.vgroupMapping.my_test_tx_group保持一致 service: vgroup-mapping: my_test_tx_group: default disable-global-transaction: false registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 group: "SEATA_GROUP" # namespace: "seed-local" config: type: nacos nacos: server-addr: 127.0.0.1:8848 group: "SEATA_GROUP" # namespace: "seed-local"
五、回滚案例
@GlobalTransactional(rollbackFor = Exception.class) public void updateTest() { String sql = "update tbl_card set amount=amount+1 where owner='b'"; jdbcTemplate.update(sql); System.out.println(1/0); }