Alibaba Seata 学习日记(AT 模式详解)
AT 模式详解
AT 模式的特点就是对业务无入侵式,整体机制分二阶段提交。
- 两阶段提交协议的演变。
- 一阶段 : 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
也就是对事物和 undo_log 直接进行提交,然后释放本地资源
- 二阶段 :
- 提交异步化,非常快速地完成。
如果提交只需要删除 回滚日志和事物表中的数据即可
- 回滚通过一阶段的回滚日志进行反向补偿。
如果回滚,需要按照回滚日志表中的数据进行反向的补偿,来达到回滚的目的
- 提交异步化,非常快速地完成。
- 一阶段 : 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
在AT 模式下,用户只需关注自己的 业务 SQL,用户的业务 SQL 作为第一阶段, Seata 框架回自动生成事物的二阶段提交和回滚操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-odRrWuGI-1625326389680)(https://www.it235.com/assets/img/image-20210215181643407.c55e8abc.png)]
Seata 具体实现步骤
- Transaction Manager(事物管理器)使用 @GlobalTransaction 进行全局事务开启、提交、回滚
- Transaction Manager 开始 RPC 调用远程服务。
- Resource Manager 端 seata-client 通过扩展 DataSourceProxy,实现自动生成 UNDO_LOG 于 TC 上报。
- Transaction Manager 告知 Transaction Coordinator 提交/回滚全局事务
- Transaction Coordinator 通知 Resource Manager 各自致性 commit/rollback 操作,同时清除 undo_log。
搭建 Seata TC 协调者
1.点击此处下载最新版本,并解压缩
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8DjpgKO-1625326389683)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625220112945.png)
-
修改 conf/file.conf 文件
-
将 mode=“file” 修改为 mode=“db”
## transaction log store, only used in seata-server store { ## store mode: file、db、redis 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.192.128:3306/seata" user = "root" password = "123456" 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 } }
-
数据库对应上述配置新建
seata
的DB,并分配给指定的用户权限[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hbwe9r3M-1625326389684)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625220727082.png)
-
初始化 table, 注意此处一定要初始化,并且 1.0 之后的版本没有 sql 脚本,这里贴出
-- the table to store GlobalSession datadrop table if exists `global_table`;create table `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`));-- the table to store BranchSession datadrop table if exists `branch_table`;create table `branch_table` ( `branch_id` bigint not null, `xid` varchar(128) not null, `transaction_id` bigint , `resource_group_id` varchar(32), `resource_id` varchar(256) , `lock_key` varchar(128) , `branch_type` varchar(8) , `status` tinyint, `client_id` varchar(64), `application_data` varchar(2000), `gmt_create` datetime, `gmt_modified` datetime, primary key (`branch_id`), key `idx_xid` (`xid`));-- the table to store lock datadrop table if exists `lock_table`;create table `lock_table` ( `row_key` varchar(128) not null, `xid` varchar(96), `transaction_id` long , `branch_id` long, `resource_id` varchar(256) , `table_name` varchar(32) , `pk` varchar(36) , `gmt_create` datetime , `gmt_modified` datetime, primary key(`row_key`));-- the table to store seata xid data-- 0.7.0+ add context-- you must to init this sql for you business databese. the seata server not need it.-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)-- 注意此处0.3.0+ 增加唯一索引 ux_undo_logdrop table `undo_log`;CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-
初始化后的3张表如下
-
修改 registry.conf 文件
这个文件内容分为 registry 和 config 2部分,我们使用之前学过的 nacos 注册中心,你也可以采用默认的 file 方式。
-
将
registry
和config
部分由 type=“file” 都换成 type=“nacos” (注意是2处地方) -
将 2 处的
nacos
配置根据实际情况需要调整参数,本机启动 nacos 则不需要调整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" group = "SEATA_GROUP" namespace = "" 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 type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" 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" }}
-
-
修改 conf/logback.xml 的文件(可选操作)
<property name="LOG_HOME" value="../logs/seata"/>
-
双击
bin/seata-server.bat
文件启动服务(有可能出现闪退) -
为了方便更加清晰的日志,建议使用
cmd
的方式。- 打开
cmd
窗口,使用 cd 命令切换到seata
的bin
目录 - 输入
seata-server.bat
回车
- 打开
-
启动成功的结果如下
-
使用 nacos 作为 Seata 的配置中心
思考
如果不用 Nacos, 那 Seata 的默认配置中心在哪?
我们编辑 registry.conf
找到 config
部分,内容如下:
config { # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { ...... } consul { ...... } apollo { ...... } zk { ...... } etcd3 { ...... } -- 重点 file { name = "file.conf" }}
就是 file.conf
文件,接下来我们不再依靠文件而是 nacos,要让 seata 使用 nacos 作为配置中心,我们需要按照以下步骤进行操作。
-
保证你的
nacos
服务已经启动,且可正常连接。 -
添加命名空间,获取
namespace
的 id, 这里是ead46517-79ec-4396-909a-13b09563de93
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-42XTnk6e-1625326389686)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625224107541.png)
-
修改
registry.conf
文件中的registry
部分type=“nacos”,同时修改nacos
部分的配置nacos { serverAddr = "127.0.0.1:8848" namespace = "ead46517-79ec-4396-909a-13b09563de93" group = "SEATA_GROUP" username = "nacos" password = "nacos" }
-
修改
registry.conf
文件中的config
部分type=“nacos”,同时修改nacos
部分的配置nacos { serverAddr = "127.0.0.1:8848" namespace = "ead46517-79ec-4396-909a-13b09563de93" group = "SEATA_GROUP" username = "nacos" password = "nacos" }
-
下载
nacos-config.脚本
和config.txt
,点击进入下载页-
nacos-config.sh 和 nacos-config.py 选择一个:在 seata 目录下新建 script 目录,将
nacos-config.sh
放入script
目录下 -
config.txt
: 该文件存放在 seata 目录下,与conf、lib
目录同级, seata 的非常全的配置内容,可以通过nacos-config.sh
脚本推送到nacos
配置中心 -
修改
config.txt
的内容,下述 1 处暂时无需修改, 2处需要换成你的 DB 连接路径[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21Qr5Vnc-1625326389686)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625225302229.png)
-
打开
git bash
或linux
类命令行,执行 sh 脚本 (注意脚本释放有执行的权限)# -h 主机,你可以使用localhost,-p 端口号 你可以使用8848,-t 命名空间ID,-u 用户名,-w 密码$ sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t ead46517-79ec-4396-909a-13b09563de93 -u nacos -w nacos
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FDrMBMa4-1625326389687)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625225845189.png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wF5Z6BqE-1625326389688)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625225859701.png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gfgAV0Qt-1625326389688)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625225933298.png)
-
编写 AT 模式代码
Resource Manager 实现
-
创建订单和库存服务的DB
-- 库存服务DB执行CREATE TABLE `tab_storage` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `total` int(11) DEFAULT NULL COMMENT '总库存', `used` int(11) DEFAULT NULL COMMENT '已用库存', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('1', '96', '4');INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('2', '100','0');-- 订单服务DB执行CREATE TABLE `tab_order` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `user_id` bigint(11) DEFAULT NULL COMMENT '用户id', `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `count` int(11) DEFAULT NULL COMMENT '数量', `money` decimal(11,0) DEFAULT NULL COMMENT '金额', `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完成', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oS3MKNAU-1625326389688)(Alibaba Seata 学习日记(AT 模式详解)].assets/image-20210625230817562.png)
-
各数据库加入 undo_log 表
CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-
添加
seata
pom.xml 依赖<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>demo01</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> </parent> <groupId>com.xssh.seata</groupId> <artifactId>seata-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <name>seata-parent</name> <description>seata演示demo</description> <properties> <java.version>1.8</java.version> </properties></project>
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>demo01</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE