Spring Cloud实战 | 第十一篇 :Spring Cloud + Nacos整合Seata 1.4.1最新版本实现微服务架构中的分布式事务
1、环境准备
1.1 Nacos安装和配置
查看《nacos安装》文章
配置命名空间ID自定义为seata_namespace_id,后面需要
1.2 Seata数据库创建
创建数据库名为seata,执行Seata的Github官方源码中提供的MySQL数据库脚本 MySQL脚本地址:
https://github.com/seata/seata/blob/1.4.1/script/server/db/mysql.sql
-- -------------------------------- 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_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- 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 = utf8;
-- the table to store lock data
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;
二. seata-server安装
查看最新Seata版本 查看地址:
https://registry.hub.docker.com/r/seataio/seata-server/tags?page=1&ordering=last_updated
1、这里使用版本为1.4.1版本,复制指令获取版本镜像
docker pull seataio/seata-server:1.4.1
2、启动临时容器
docker run -d --name seata -p 8091:8091 seataio/seata-server:1.4.1
3、从临时容器获取到 registry.conf 配置文件
mkdir /home/docker-compose/seata
docker cp seata:/seata-server/resources/registry.conf /home/docker-compose/seata
4、修改registry.conf配置,类型选择nacos,namesapce为上文中在nacos新建的命名空间id即seata_namespace_id,精简如下:
registry {
type = "nacos"
loadBalance = "RandomLoadBalance"
loadBalanceVirtualNodes = 10
nacos {
application = "seata-server"
serverAddr = "192.168.147.129:8848"
group = "SEATA_GROUP"
namespace = "seata_namespace_id"
cluster = "default"
username = "nacos"
password = "nacos"
}
}
config {
type = "nacos"
nacos {
serverAddr = "192.168.147.129:8848"
namespace = "seata_namespace_id"
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
}
}
5、 安排好 registry.conf 之后,删除临时容器
docker rm -f seata
6、 Nacos配置Seata所需配置
获取 config.txt 即所有配置信息。 链接
:https://github.com/seata/seata/blob/develop/script/config-center/config.txt
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.rpcRmRequestTimeout=5000
transport.rpcTmRequestTimeout=10000
transport.rpcTcRequestTimeout=10000
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.default_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.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
store.mode=file
store.lock.mode=file
store.session.mode=file
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://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
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
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
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
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
修改部分配置
service.vgroupMapping.mall_tx_group=default
store.mode=db
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.1.188:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
获取配置推送脚本nacos-config.sh 地址链接:
https://github.com/seata/seata/blob/develop/script/config-center/nacos/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
如果config.txt 和nacos-config.sh放在同一目录下,需要修改下图路径:
修改完成后执行推送命令
bash nacos-config.sh -h 192.168.147.129 -p 8848 -g SEATA_GROUP -t seata_namespace_id -u nacos -w nacos
登录nacos查看
7、 做完上述准备工作之后,接下来最后一步:启动Seata容器
docker run -d --name seata --restart=always -p 8091:8091 \
-e SEATA_IP=192.168.147.129 \
-e SEATA_CONFIG_NAME=file:/seata-server/resources/registry.conf \
-v /home/docker-compose/seata/registry.conf:/seata-server/resources/registry.conf \
-v /home/docker-compose/seata/logs:/root/logs \
seataio/seata-server:1.4.1
三. Seata客户端
1. 添加undo_log表
Seata官方Github源码库undo_log表脚本地址:
https://github.com/seata/seata/blob/1.4.1/script/client/at/db/mysql.sql
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `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
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
别在项目的涉及到的数据库执行脚本创建 undo_log 表
2. 添加依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
io.seata
seata-all
io.seata
seata-spring-boot-starter
io.seata
seata-all
1.4.1
io.seata
seata-spring-boot-starter
1.4.1
3、添加yml配置
# 分布式事务配置
seata:
tx-service-group: mall_tx_group
enable-auto-data-source-proxy: true
registry:
type: nacos
nacos:
server-addr: 192.168.147.129:8848
namespace: seata_namespace_id
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: 192.168.147.129:8848
namespace: seata_namespace_id
group: SEATA_GROUP
4、启动服务前配置 VM参数
-Dproject.name="orderMallServer"
有订阅者服务说明配置成功。
5、order,member,product三个项目各自实现测试代码和数据库
具体代码见gitee 标签 0.0.1的代码
https://gitee.com/good_luck_sir/microservice-learning/repository/archive/0.0.1
6、可先看第7步,有问题在看这一步操作。添加kryo依赖,解决seata1.4.1序列化问题(修改nacos配置client.undo.logSerialization的值为kryo)
com.esotericsoftware
kryo
4.0.2
de.javakaffee
kryo-serializers
0.44
6、使用postman测试
body内容如下,根据具体情况修改部分内容后测试。
{
"buyProductVOList": [{
"productId":1,
"productPrice":122222,
"count":1
}],
"point":30,
"id":1,
"orderSn":"20211227000031",
"totalAmount":12.00,
"totalQuantity":1,
"sourceType":1,
"status":101,
"remark":"测试",
"memberId":1,
"couponId":null,
"couponAmount":0.00,
"freightAmount":0.00,
"payAmount":1000.00,
"payTime":{{startTime}},
"payType":1,
"deliveryTime":{{startTime}},
"receiveTime":{{startTime}},
"commentTime":{{startTime}},
"deleted":0,
"createTime":{{startTime}},
"updateTime":{{startTime}}
}
可以设定失败,查看是否事务会回滚。
查看seata日志、和数据库数据。