初识Seata
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案,这四种模式的具体解释可以查看官方文档
下载地址:https://github.com/seata/seata/releases/download
官方文档:http://seata.io/zh-cn/docs/user/quickstart.html
Seata基本使用
一、检查环境
因为之后的设置可能要配置注册中心和配置中心,所以如果需要用到要提前准备好,我这里的注册中心用的Nocos,配置中心用的file.conf
二、引入依赖
这里注意版本问题
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>
三、YML配置文件
seata.enabled属于1.0新特性,可以不用引入file.conf 和 registry.conf,但我这里使用的是引入文件的方式
# 其他配置项省略
spring:
# db相关配置
datasource:
# 是否开启seata事务
seata: true
alibaba:
# 分布式事务
seata:
# 激活自动配置,使得我们可以在yaml/properties文件中配置
enabled: true
# enable-auto-data-source-proxy: true
# 事务分组名
tx-service-group: my_test_tx_group
四、创建undo_log表
SEATA AT模式需要undo_log表,所以在你自己的数据库中创建该表保证事务正常运行
-- mysql 建表语句
CREATE TABLE IF NOT EXISTS `undo_log`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
`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 NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
五、引入文件
resources目录下创建 registry.conf和file.conf 两个文件(classpath路径下)
1. registry.conf文件(主要是设置注册中心和配置中心)
# 注册中心
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
#loadBalance = "RandomLoadBalance"
#loadBalanceVirtualNodes = 10
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
namespace = ""
# seata-server在nacos的集群名
cluster = "default"
username = ""
password = ""
}
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
#type = "nacos"
type = "file"
# 使用nacos作为配置中心
nacos {
serverAddr = "127.0.0.1:8848"
# nacos命名空间id,""为nacos保留public空间控件,用户勿配置namespace = "public"
namespace = ""
group = "SEATA_GROUP"
# group = "DEFAULT_GROUP"
username = "nacos"
password = "nacos"
# cluster = "default"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://127.0.0.1:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
# 使用file.conf作为客户端配置解析
file {
name = "file.conf"
}
}
2. file.conf(具体的配置)
# 事务分组配置,TC的地址配置,用于获取TC的地址
service {
# 注意 my_test_tx_group 为我们这里定义的事务的分组名
# 后面为registry.conf中使用的注册中心的application
# "default"是TC服务端集群的名称,将来通过注册中心获取TC地址
# 1.1.0版本以下是vgroup_mapping
vgroupMapping.my_test_tx_group = "default"
default.grouplist = "127.0.0.1:8091"
# 服务降级,默认关闭,如果开启,当业务重试多次失败后会放弃全局事务
enableDegrade = false
# 全局事务开关,默认false。false为开启,true为关闭
disableGlobalTransaction = false
}
## 事务日志存储,仅用于seata服务器
store {
## 存储方式:file和db,file在文件中存储,db在数据库中存储
mode = "file"
## 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) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "mysql"
password = "mysql"
minConn = 5
maxConn = 30
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
client {
# 资源管理器配置
rm {
# 二阶段提交默认是异步执行,这里指定异步队列的大小
asyncCommitBufferLimit = 10000
# 全局锁配置
lock {
# 校验或占用全局锁重试间隔,默认10,单位毫秒
retryInterval = 10
# 校验或占用全局锁重试次数,默认30次
retryTimes = 30
# 分支事务与其它全局回滚事务冲突时锁策略,默认true,优先释放本地锁让回滚成功
retryPolicyBranchRollbackOnConflict = true
}
# 一阶段结果上报TC失败后重试次数,默认5次
reportRetryCount = 5
tableMetaCheckEnable = false
reportSuccessEnable = false
}
# 事务管理器配置
tm {
# 一阶段全局提交结果上报TC重试次数,默认1
commitRetryCount = 5
# 一阶段全局回滚结果上报TC重试次数,默认1
rollbackRetryCount = 5
}
# undo_log的配置
undo {
# 是否开启二阶段回滚镜像校验,默认true
dataValidation = true
# undo序列化方式,默认Jackson
logSerialization = "jackson"
# 自定义undo表名,默认是undo_log
logTable = "undo_log"
}
# 日志配置
log {
# 出现回滚异常时的日志记录频率,默认100,百分之一概率。回滚失败基本是脏数据,无需输出堆栈占用硬盘空间
exceptionRate = 100
}
}
# 与TC交互的配置
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
# client和server通信心跳检测开关,默认true开启
heartbeat = true
# 客户端事务消息请求是否批量合并发送
enableClientBatchSendRequest = true
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThread-prefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
# client和server通信编解码方式,seata(ByteBuf)、protobuf、kryo、hession、fst,默认seata
serialization = "seata"
# client和server通信数据压缩方式,none、gzip,默认none
compressor = "none"
}
六、代理数据源
使用Seata对数据源进行代理,需要注意的是,如果是比较高的版本(1.0)不需要我们去手动配置代理数据源
@Configuration
public class Config {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
@Primary
public DataSourceProxy dataSourceProxy(DruidDataSource dataSource) {
return new DataSourceProxy(dataSource);
}
/**
* 使用mybatis
*/
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
// 因为使用的是mybatis,这里定义SqlSessionFactoryBean
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 配置数据源代理
sqlSessionFactoryBean.setDataSource(new DataSourceProxy(dataSource));
return sqlSessionFactoryBean.getObject();
}
/**
* 使用mybatis-plus
*/
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
// 业务中引入了mybatis-plus,所以要使用特殊的SqlSessionFactoryBean
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
// 代理数据源
sqlSessionFactoryBean.setDataSource(new DataSourceProxy(dataSource));
// 生成SqlSessionFactory
return sqlSessionFactoryBean.getObject();
}
}
六、测试
简单测试一下,使用注解@GlobalTransactional
1. 在业务实现类的方法上加上 @GlobalTransactional(rollbackFor=Exception.class)
2. 方法内部业务操作之后人为造个异常
3. 运行方法查看业务操作是否回滚
可能的问题:
问题一:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.seata.spring.annotation.GlobalTransactionScanner]
Caused by: io.seata.common.exception.NotSupportYetException: not support register type: null
解决: 如果没在classpath下创建两个文件会出现异常
问题二:
io.seata.common.exception.FrameworkException: can not register RM,err:can not connect to services-server.
Caused by: io.seata.common.exception.FrameworkException: connect failed, can not connect to services-server.
解决: 启动程序前需要先启动seata服务端
问题三:
控制台不停打印 no available service ‘default’ found, please make sure registry config correct
解决: 1. 首先确认文件中事务分组名是否能对应上
2.看file.conf中使用的是vgroup_mapping还是vgroupMapping,因为1.1.0版本之下是vgroup_mapping,1.1.0版本开始修改为vgroupMapping
3.修改YML配置文件enabled属性
spring:
alibaba:
seata:
enabled: true
问题四:
控制台不停打印 com.alibaba.nacos.client.naming
解决: 设置指定的日志输出级别
logging:
level:
com:
alibaba:
nacos:
client:
naming: warn
问题五:
Seata怎样用我们创建的undo_log表的
解决: undo_log表是事务用来回滚的表,在回滚后控制台可以看到的事务完成后undo_log日志被删除掉了(undo_log deleted with GlobalFinished)。如果程序执行完再看,一般就是空表,所以要想查看表中数据,就要在操作完数据库和自定义异常的中间debug查看