提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:新手向,有异议请不吝赐教:
搭建项目时遇到了一些问题,在网上找到了不少关于seata的配置文章,有的博主用老版本的思路在发文章,结果做了很多无用功,踩了不少坑总算是实现了seata的分布式事务,在此将这个试错demo项目分享出来,供各位摸索的同学参考。
以下不罗嗦,直接从nacos,seata搭建开始
一、Nacos环境
从官网下载的nacos,配置standlone模式运行,完毕后环境如下:
访问地址:http://192.168.5.210:8848/nacos
用户名:nacos
密码:nacos
二、Seata环境
1.下载Seata
来到阿里巴巴Github站,找到“版本说明”页:
本次使用的SpringBoot版本是2.6.8,SpringCloud版本是2021.0.1.0,所以选定的Seata版本是1.4.2,不要擅自尝试修改其他版本,太浪费时间了。
到Seata下载页,找到1.4.2版本进行下载,这里任意选择合适的版本下载,我的运行环境在linux选择tar.gz。
2.配置Seata
核心配置文件是file.conf和registry.conf
由于是1.4.2,不需要执行所谓的sh脚本,只需要贴入官方的配置到nacos即可,这里的弯路着重强调,没有必要走。
file.conf代码如下:
store {
mode = "db"
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://192.168.5.210:3306/seata?rewriteBatchedStatements=true"
user = "xxx"
password = "xxx"
minConn = 5
maxConn = 100
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 = "nacos"
nacos {
application = "seata-server"
serverAddr = "192.168.5.210:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "192.168.5.210:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}
请务必留意上文配置中的dataId = seataServer.properties
这个dataId需要配到nacos中,下面我们开始配置这个
由于namespace未指定特殊值,留空表示使用public
我们在nacos配置列表项的public命名空间中,新增配置文件"seataServer.properties",配置类型选择为"Properties",新建时确保名称如下:
Data ID: seataServer.properties
Group: SEATA_GROUP
然后在编辑区贴入示例内容页中的内容。
贴入的内容有部分关键地方需要修改,请留意:
...省略配置...
#修改数据驱动
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://数据库服务ip:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=xxx
store.db.password=xxx
...省略配置...
# 自定义命名vgroupMapping default-tx-group java项目要用,切记
service.vgroupMapping.default-tx-group=default
service.default.grouplist=seta服务ip:8091
...省略配置...
到这里为止seta服务的配置OK了,接下来需要注意seata数据库的表创建。
seata数据库表分为2个部分,一个部分是上文中配置的数据库持有,另一个部分是使用seata的每一个数据库必须持有的表。
我们先确认第一部分,建表语句如下:
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;
-- 分支表
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;
-- 锁定表
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),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = INNODB
DEFAULT CHARSET = utf8;
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);
第二部分,用到seata的数据库中,需要创建表,建表语句如下:
--日志文件表--
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT 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
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
至此关于seata的配置告一段落,接下来我们开始配置示例项目。
三、基于SpringBoot项目使用Seata
很多同学在这里面遇到了麻烦,要不就是找不到服务,要不就是无法启动。
核心部分就是application.yml文件,java代码暂且不表,相信你们肯定不会有问题。
spring:
application:
name: seataServer
redis:
host: 192.168.5.210
port: 6379
password: xxx
datasource:
dynamic:
druid:
initial-size: 5
min-idle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall,slf4j
# connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
datasource:
# 主库数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.5.210:3306/cloud-xxx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: xxx
password: xxx # seata_order数据源
order:
username: xxx
password: xxx
url: jdbc:mysql://192.168.5.210:3306/seata_order?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
# seata_account数据源
account:
username: xxx
password: xxx
url: jdbc:mysql://192.168.5.210:3306/seata_account?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
# seata_product数据源
product:
username: xxx
password: xxx
url: jdbc:mysql://192.168.5.210:3306/seata_product?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
seata: true
cloud:
nacos:
server-addr: 192.168.5.210
username: nacos
password: nacos
server:
port: 8081
# seata配置
seata:
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: default-tx-group
# 关闭自动代理
enable-auto-data-source-proxy: false
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
default-tx-group: default
vgroupMapping:
default-tx-group: default
# 分组和 Seata 服务的映射
grouplist:
default: 192.168.5.210:8091
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.5.210:8848
username: nacos
password: nacos
cluster: default
group: SEATA_GROUP
config:
type: nacos
nacos:
data-id: seataServer.properties
server-addr: 192.168.5.210:8848
group: SEATA_GROUP
username: nacos
password: nacos
# mybatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.xxx.seata
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mapper/**/*.xml
下面开始说核心配置:
- druid数据源,由于本示例项目在1个项目中模拟多个数据源,所以这里配置了动态数据源,如果没有这个需求可以无视本配置
- tx-service-group: default-tx-group,这个很关键,算是核心入口了,这个default-tx-group在nacos配置文件中指定过,所以此处需要指定这个组,包括下面的seata.vgroup-mapping也是如此,不能错不能少。
- seata.registry配置,这个说的是项目在nacos中,怎么找seata服务器,所以这里的application一定要亲眼在nacos的“服务管理”->“服务列表”中找到启动中的seata服务,名字要一样,比如我这里叫做seata-server,在nacos服务列表中至少要有1个示例,不然会出现找不到服务。
- seata.config配置,这里说的是seata客户端要读取的配置地址,所以指定的data-id必须要在nacos中的“配置管理”->“配置列表”中找到,data-id要一模一样,包括命名空间和组名,不能乱不能少。
此外,不少同学在配置完成后,会出现Type id handling not implemented for type java.lang.Object这样的报错,这个是Seata1.4版本自身的问题,据说1.5版会修复。解决方法如下:
- 在nacos中修改seata的序列化方式,修改properties内容中client.undo.logSerialization=jackson设置为kryo,同时需要配置项目依赖
<dependency> <groupId>com.esotericsoftware.kryo</groupId> <artifactId>kryo</artifactId> <version>2.24.0</version> </dependency> <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>de.javakaffee</groupId> <artifactId>kryo-serializers</artifactId> <version>0.42</version> </dependency>
- 参考https://github.com/seata/seata/pull/3228/files 或者看这个pr的做法,通过spi,自定义jackson序列化器
- 数据库日期字段从datetime修改成timestamp,不过可能还需要考虑2038年问题
总结
至此踩坑的干货已经分享完毕,各位还有不同的意见欢迎留言交流。
示例代码下载地址