Seata 分布式事物
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
详细介绍可参考文档 :seata.io/zh-cn/docs/…
下载
https://seata.io/zh-cn/blog/download.html
我这里用的是Seata 1.4.0版本
使用
解压压缩包进入到conf文件夹下.这里会有几个配置文件,修改配置
1:file.conf
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
## 将file改为db 用db处理事物 下边的db选项直接改为自己的数据源
mode = "db"
## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## database store property
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"
url = "jdbc:mysql://192.168.10.65:3308/seata"
user = "root"
password = "root"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {
host = "127.0.0.1"
port = "6379"
password = ""
database = "0"
minConn = 1
maxConn = 10
maxTotal = 100
queryLimit = 100
}
}
2:registry.conf
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
# 这里的注册中心用的是nacos,改为nacos ,之后只需要修改nacos对应的配置即可
type = "nacos"
loadBalance = "RandomLoadBalance"
loadBalanceVirtualNodes = 10
nacos {
application = "seata-server"
serverAddr = "192.168.10.65:8848" #nacos地址
group = "SEATA_GROUP" #服务注册的分组
namespace = "0af6e97b-a684-4647-b696-7c6d42aecce7" #服务注册的命名空间,不填默认是public
cluster = "default"
username = "nacos"
password = "nacos"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = 0
password = ""
cluster = "default"
timeout = 0
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
# 为了方便后期维护,配置也直接用nacos ,使用这个选项需要先把 配置信息 上传 到nacos中
type = "nacos"
# 同样需要修改nacos即可
nacos {
serverAddr = "192.168.10.65:8848"
namespace = "0af6e97b-a684-4647-b696-7c6d42aecce7"
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
namespace = "application"
apolloAccesskeySecret = ""
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
3:将配置导入到nacos
这里需要准备两个文件:
config.txt(seata的配置信息)
nacos-config.sh(把配置信息上传到nacos的脚本)
这两个文件是下载的Seata里没有的需要自己去github上下载
下载地址:
nacos-config.sh: github.com/seata/seata…
config.txt: github.com/seata/seata…
下载好之后需要把 nacos-config.sh 放到 seata/conf 文件夹下,和 registry.conf这两个文件夹同级
下载好之后需要把 config.txt 放到 seata 文件夹下, 和conf文件夹同级
3.1 修改config.txt文件
这个文件就是seata的配置信息,需要修改数据库配置和mode配置
#这里只列出几个主要修改的信息
#事物存储配置
store.mode=db # file改为db
#数据库配置
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.10.65:3308/jy-seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
#这个是事物的分组,这个可以配置多个
# 格式:
# service.vgroupMapping.改这里改成自己需要的=default
service.vgroupMapping.default_tx_group=default
3.2 运行 nacos-config.sh 脚本
sh nacos-config.sh -h 192.168.10.65 -p 8848 -g SEATA_GROUP -t 0af6e97b-a684-4647-b696-7c6d42aecce7 -u nacos -w nacos
# -h -p 指定nacos的端口地址;
# -t 指定命名空间id;(这里需要提前创建一个命名空间,因为这个配置特别多.最好不要弄到默认的...)
# -g 指定配置的分组,注意,是配置的分组(上传的配置都会到这个分组下);
# -u -w 指定nacos的用户名和密码,
创建数据库
因为上边咱们配置的是用数据库来存储事物,所以需要创建相应的数据库,总共是四个表
-- 创建seata数据库并创建表 这三个表是seata服务用的
DROP DATABASE IF EXISTS `seata`;
CREATE DATABASE `seata` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
USE `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_gmt_modified_status` (`gmt_modified`, `status`),
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(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 = utf8mb4;
--这个表是每个业务数据库中都需要有 ,用了存储回滚的数据
-- 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 = utf8mb4 COMMENT ='AT transaction mode undo table';
修改Spring Boot配置文件
# seata配置
seata:
# 默认关闭,如需启用spring.datasource.dynami.seata需要同时开启 ****************
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名 这个就是上边配置文件中的分组 *****************
tx-service-group: ${spring.application.name}-group
# 关闭自动代理
enable-auto-data-source-proxy: false
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
jy-company-document-group: default #这里要和配置文件中的一致 **************
config:
type: nacos
nacos:
serverAddr: 192.168.10.65:8848
group: SEATA_GROUP
namespace: 0af6e97b-a684-4647-b696-7c6d42aecce7
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.10.65:8848
namespace: 0af6e97b-a684-4647-b696-7c6d42aecce7
启动seata
进到bin目录直接启动 ./seata-server.bat
查看日志,seata启动正常,nacos中可以看到seata注册成功
使用:只需要早事物开启的地方加上@GlobalTransactional注解就可以了,其他方法还是加上 @Transactional(rollbackFor = Exception.class)注解
@Override
@GlobalTransactional // 重点 第一个开启事务的需要添加seata全局事务注解
public AjaxResult addUserByAddressBook(SysUser sysUser) {
AjaxResult ajaxResult = remoteUserService.addUserInfo(sysUser, SecurityConstants.INNER);
if (HttpStatus.SUCCESS == ajaxResult.getCode()) {
//在通讯录上关联用户Id
AddressBook addressBook = new AddressBook();
addressBook.setAbId(sysUser.getAddressBookId());
addressBook.setSysUserId(Long.parseLong(String.valueOf(ajaxResult.get("userId"))));
addressBookMapper.updateAddressBook(addressBook);
}
return ajaxResult;
}
/**
* 新增保存用户信息
*
* @param user 用户信息
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertUser(SysUser user) {
user.setCreateTime(new Date());
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
return rows;
}
使用中遇到的问题
1.4.0版本在使用中会遇到如果时间序列化失败的问题.如果时间格式是dataTime格式则会失败
解决放法
1:修改时间格式为timestamp格式:这种不适合已经上线了的项目.
2:修改seata序列化方式为 kryo
在项目中引入依赖
<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.44</version>
</dependency>
修改seata配置文件
<!--修改 client.undo.logSerialization 为kryo 即可-->
client.undo.logSerialization=kryo
重新启动seata
代码这里代码用的是 若依:RuoYi-Cloud进行的测试:gitee.com/y_project/R…