文章目录
前言
今天大致学完了Seata,版本对应还是挺重要的
版本对应关系
- SpringBoot—2.3.12.RELEASE
- SpringCloud—Hoxton.SR12
- SpringCloud Alibaba—2.2.7.RELEASE
- Nacos—2.0.4
- Seata—1.4.2
一、简述
- Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
1、分布式事务过程
分布式事务处理过程的一ID+三组件模型:
- Transaction ID XID 全局唯一的事务ID
- 三组件概念
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
2、处理过程
- TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
- XID在微服务调用链路的上下文中传播
- RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
- TM向TC发起针对XID的全局提交或回滚决议
- TC调度XID下管辖的全部分支事务完成提交或回滚请求
二、安装
官方下载Github
将下载好的压缩包解压到指定目录,可以先将配置文件file.conf—registry.conf进行备份
官方服务端SQL文件
官方回滚SQL文件
官方客户端file.conf®istry.conf
官方客户端配置信息config.txt
- 服务端(即seata文件)需要修改conf目录下的file.conf以及registry.conf
- file.conf主要修改的是数据存储,如果是使用数据库进行存储,需要独立的seata数据库,可以使用官方服务端SQL文件进行初始化
- registry.conf主要修改的是seata的注册地点以及配置信息的位置
- 客户端(即需要使用seata的服务模块)需要指定seata的注册地点以及配置信息的位置
- 如果使用的file方式,需要将file.conf和registry.conf放入resources目录下,文件内容可以参考官方客户端file.conf®istry.conf
- 如果使用的不是file方式(Nacos…),只需要指定地点即可
- 配置中心(eg:Nacos)需要有配置信息,具体可以参考官方教程
- 高版本(v1.4.2)只需要一个配置文件,配置文件的内容可以参考官方客户端配置信息config.txt
- 低版本的每一个配置规则独立成为一个配置文件,可以使用脚本上传配置到Nacos
- 一切以官方文档为主
1、file.conf
主要修改seata信息的存储方式,以及存储地点的一些设置
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
##信息储存模式:file--文件,db--数据库,redis--redis
mode = "db"
## rsa decryption public key
publicKey = ""
## 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.cj.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
##数据库名为seata,需要自己创建,并在创建表global_table--branch_table--lock_table,可以使用官方提供的SQL文件
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true&serverTimezone=UTC"
user = "root"
password = "123456"
minConn = 5
maxConn = 100
##具体的数据库表:global--branch--lock
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
2、数据库
- 如果在file.conf中指定了使用数据库存储信息,这需要对数据库进行初始化
- seata服务端需要的数据库名为seata,需要三个表:global_table–branch_table–lock_table
- 每一个微服务需要调用的数据库都要有一个回滚表(同一个数据库的多个表可以共用一个回滚表):undo_log
服务端表的SQL文件(数据库seata)
官方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_status_gmt_modified` (`status` , `gmt_modified`),
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(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
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);
回滚表(每一个微服务需要调用的数据库)
官方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 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 = utf8mb4 COMMENT ='AT transaction mode undo table';
3、registry.conf
# seata注册方式
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
#服务端注册进nacos的服务名
application = "seata-server"
#nacos地址
serverAddr = "localhost:8848"
#服务端注册进nacos的group
group = "SEATA_GROUP"
#服务端注册进nacos的namespace,默认值为public
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
······
}
#配置信息获取方式
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
#nacos地址
serverAddr = "localhost:8848"
#配置信息所在的namesapce,默认为public
namespace = ""
#配置信息所在的group
group = "SEATA_GROUP"
#配置信息的dataId
dataId = "seataServer.properties"
username = "nacos"
password = "nacos"
}
······
}
三、使用
官方手册
本地@Transactional
全局@GlobalTransactional
使用的时候是需要使用一个@GlobalTransactional
注解,使用在事务最开始的业务方法上即可
1、POM
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!--本身自带的seata版本可能不匹配,需要额外引入匹配的版本-->
<exclusions>
<exclusion>
<artifactId>seata-spring-boot-starter</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
2、yml
seata的配置信息和注册方式为nacos
seata:
#该值要和配置文件中的service.vgroupMapping.XXX=default相匹配
tx-service-group: my-test-tx-group #config.txt 里面的 group
service:
vgroup-mapping:
my-test-tx-group: default
enabled: true
#设为true就不用再手动配置数据源代理
#如果为false则需要手动配置数据代理,数据源代理应为seata下的类:io.seata.rm.datasource.DataSourceProxy
#seata对数据源代理才能完成事务控制
enable-auto-data-source-proxy: true
#seata客户端获取配置信息
#高版本使用一个默认配置信息:dataId:seataServer.properties
#低版本使用多个配置信息,一条配置为一个配置信息,dataId为配置名
config:
#获取配置信息的方式
#如果为file方式,则要在resource目录下创建文件file.conf,该文件下写入seata客户端的配置信息
#如果为nacos方式,则要在naocs中创建配置信息
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
#namespace默认为public
#使用public为命名空间不能写成-->namespace: public/"public",这样会获取不到配置信息
namespace: ""
#nacos中的配置信息的dataId
data-id: seataServer.properties
username: nacos
password: nacos
#seata服务端注册的方式
registry:
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
group: SEATA_GROUP
namespace: ""
username: nacos
password: nacos
3、客户端配置信息
- 配置信息或注册方式为file
- 官方file.conf
- 官方registry.conf
- 修改file.conf和registry.conf里的部分配置信息,并放入resources目录下
- 配置信息为nacos
- 在nacos中添加配置信息
- dataId为seataServer.properties(v1.4.2)
- group的值随意,但是需要客户端可以找到该配置
- 具体配置内容可以参考官方config.txt
file.conf或seataServer.properties的一个重要配置
- service.vgroupMapping.default_tx_group=default
default_tx_group可以随意指定,但是该值必须要和客户端的yml里的值一致,相同才能表明是同一个事务组
- file.conf/seataServer.properties
- service.vgroupMapping.XXX=default
- application.yml
- seata.tx-service-group: XXX
4、具体效果
-
启动Nacos
- 运行bin/startup.cmd
-
启动Seata
- 运行bin/seata-server.bat
- 运行bin/seata-server.bat
-
seata数据库
- 需要有指定结构的四个表:branch_table—distributed_lock—global_table—lock_table
- 需要有指定结构的四个表:branch_table—distributed_lock—global_table—lock_table
-
服务操作的数据库
- 每一个数据库都需要有指定结构的undo_log表
- 每一个数据库都需要有指定结构的undo_log表
-
Nacos中有seata客户端需要的配置
-
服务的application.yml指定的seata的一些配置要求
-
启动服务
5、使用
在一个事务组中,只需要使用@GlobalTransactional注解在事务最开始的方法上即可
如果调用其他服务出错时,会将该事务所以涉及的数据进行回滚
@GlobalTransactional的name属性设置一个独特的名字即可
@GlobalTransactional的rollbackFor属性为回滚条件
@Override
//Exception.class表明出现了任意异常都会对数据进行回滚
@GlobalTransactional(name = "test-create-order",rollbackFor = Exception.class)
public void create(Order order)
{
log.info("----->开始新建订单");
//1 新建订单
orderMapper.create(order);
//2 扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(),order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//3 扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//4 修改订单状态,从零到1,1代表已经完成
log.info("----->修改订单状态开始");
orderMapper.update(order.getUserId(),0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了,O(∩_∩)O哈哈~");
}
6、回滚成功
如果对服务进行debug,可以在保存数据的地址(eg:数据库…)看到每一个相关数据库的undo_log存在数据,seata数据库的表也存在数据
如果服务回滚完成,会将这些数据删除
官方说明