Seata1.3(AT模式) + Zookeeper +Dubbo 整合
Seata下载
注:AT模式
我下载的是源码
- 建立数据库 SEATA ,AT 模式需要 UNDO_LOG 表;每个和业务相关的库都要有一张 UNDO_LOG 表,后续打断点调试,能看到里面的暂存数据
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for branch_table
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`transaction_id` bigint(20) NULL DEFAULT NULL,
`resource_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`lock_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`branch_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`status` tinyint(4) NULL DEFAULT NULL,
`client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`branch_id`) USING BTREE,
INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`transaction_id` bigint(20) NULL DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_service_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`timeout` int(11) NULL DEFAULT NULL,
`begin_time` bigint(20) NULL DEFAULT NULL,
`application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`xid`) USING BTREE,
INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
`row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_id` mediumtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`branch_id` mediumtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pk` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`row_key`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
- 源码导入idea 报错:
io.seata.codec.protobuf.generated不存在 !
解决方案:本地执行./mvnw clean install -DskipTests=true (Mac,Linux) 或 mvnw.cmd clean install -DskipTests=true (Win)
这个官网有讲到!!!! - 修改script/config-center/下的 config.txt 文件
主要修改 store.mode=db 以及数据库的连接信息
下边这行代码是重点service.vgroupMapping.doushanglai-service_tx_group=default
旧版本是vgroup_mapping,新版本是 vgroupMapping
如果看的帖子太多,配置文件新老掺和在一起的话要注意
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.doushanglai-service_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.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
store.mode=db
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=hikari
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.0.162:3306/seata?useUnicode=true
store.db.user=seata
store.db.password=&Seata12
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.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
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.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
- 启动注册中心,把seata 配置写入到注册中心
如果你本地的注册中心用的nacos,在seata项目下的script/
config-center/ 下 执行脚本。
sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u username - w password
这一步是把seata 的配置写进注册中心,项目此模块下的 md文档上有写。
nacos的话应该按照文档来, 一套流程走到底。
重点本人用的 Zk,同样执行上边的脚本命令
zk-config.sh -h localhost -p 2181 -z "/Users/zhangchenghui/zookeeper-3.4.14
这个脚本命令执行后,在Zk的节点上只看到了 /seata 节点,下边的子节点全是空的。
所以怀疑1.3 的zk.sh 脚本有问题,仅仅是怀疑,也可能是我自己没搞好,解决方案,代码如下:
代码写入 seata 节点(拷贝别人的)
import com.google.common.base.Charsets;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
import org.apache.zookeeper.CreateMode;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Set;
public class ZkDataInit {
private static volatile ZkClient zkClient;
public static void main(String[] args) {
if (zkClient == null) {
zkClient = new ZkClient("127.0.0.1:2181", 6000, 2000);
zkClient.setZkSerializer(new ZkSerializer() {
@Override
public byte[] serialize(Object o) throws ZkMarshallingError {
return String.valueOf(o).getBytes(Charsets.UTF_8);
}
@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
return new String(bytes, Charsets.UTF_8);
}
});
}
if (!zkClient.exists("/seata")) {
zkClient.createPersistent("/seata", true);
}
//获取key对应的value值
Properties properties = new Properties();
// 使用ClassLoader加载properties配置文件生成对应的输入流
// 使用properties对象加载输入流
try {
File file=new File("C:\\Users\\zjj\\Desktop\\seata-1.3.0\\script\\config-center\\config.txt");
InputStream in = new FileInputStream(file);
properties.load(in);
Set<Object> keys = properties.keySet();
for (Object key : keys) {
putConfig(key.toString(), properties.get(key).toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param dataId
* @param content
* @return
*/
public static boolean putConfig(final String dataId, final String content) {
Boolean flag = false;
String path = "/seata/" + dataId;
if (!zkClient.exists(path)) {
zkClient.create(path, content, CreateMode.PERSISTENT);
flag = true;
} else {
zkClient.writeData(path, content);
flag = true;
}
return flag;
}
运行完之后,查看zk的 /seata 节点下是否有子节点,如图所示就对了
- 修改seata-server 下的 file.conf 和 registry.conf
主要修改mode以及数据库连接信息和注册方式
file.conf
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "db"
## database store property
db {
datasource = "hikari"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://192.168.0.162:3306/seata"
user = "seata"
password = "&Seata12"
minConn = 5
maxConn = 30
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
registry.conf
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "zk"
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 60000
connectTimeout = 20000
username = ""
password = ""
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "zk"
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 60000
connectTimeout = 20000
username = ""
password = ""
}
file {
name = "file.conf"
}
}
- 以上确保配置没有问题,启动Server main() 直接启动
如果有一连串的getconfig 错误 检查 注册中心节点是否配置成功 - 项目配置
springboot配置
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: doushanglai-service_tx_group
enable-auto-data-source-proxy: true
config:
type: zk
zk:
serverAddr: 127.0.0.1:2181
registry:
type: zk
zk:
application: ${spring.application.name}
server-addr: 127.0.0.1:2181
启动dubbo A服务 和 B服务
测试代码如下
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public JsonResult test() {
System.out.println("开始全部事务============================》");
goodsService.test();
LambdaUpdateWrapper<Task> l = Wrappers.lambdaUpdate();
Task t = new Task();
t.setGoodsId(999L);
this.baseMapper.update(t, l);
throw new RuntimeException("测试报错");
}
@Override
public JsonResult test() {
System.out.println("进入商品TEST事务之前=======================================》》》");
LambdaUpdateWrapper<Goods> u = Wrappers.lambdaUpdate();
Goods g = new Goods();
g.setStock("200");
this.baseMapper.update(g, u);
System.out.println("进入商品TEST事务之后=======================================》》》");
return null;
}
@PostMapping("test")
@ApiOperation(value = "测试分布式事务【A调用B,A抛异常,AB数据都回滚】", notes = "测试分布式事务【A调用B,A抛异常,AB数据都回滚】", produces = "application/json")
public JsonResult detailAuthFeeList() {
return JsonResult.buildResult(taskService.test());
}
结果A服务 Log
B服务 Log
数据库数据 goods表
Task表
可以在调用端打断点,观察数据库表数据的变化,还有seata 几张表的数据变化,这样更容易理解点。
初步使用已经成功,还在更深入的了解中
配置参考:官方教学
总结:
- 注册中心的seata配置要确定没问题,才能往下继续进行,其他注册中心没试过,seata 源码zookeeper 的写配置的脚本一直写不成功
- config.txt 里 service.vgroupMapping.doushanglai-service_tx_group=default 的 doushanglai-service_tx_group 配置啥样的 项目对应的服务配置就要写成啥样,修改完config.text的内容后要重新把节点写入一下注册中心(zk,其他注册中心不是很清楚)
- seata no available service ‘null’ found, please make sure registry config correct 这个错误检查 上边的service.vgroupMapping.doushanglai-service_tx_group=default 是否正确,是否能在注册中心的节点上找到
- 项目结构不一样,所以仅供参考