![eee3429759044e7d9e7ae60dd56d1653.png](https://i-blog.csdnimg.cn/blog_migrate/720ce14e1c6dfa23dbb1785d6d01229c.jpeg)
前言
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。对于Seata不太了解的朋友,可以看下我之前写的文章: 微服务开发的最大痛点-分布式事务SEATA入门简介。
AT模式
AT模式怎么理解
AT模式下,每个数据库被当做是一个Resource,Seata 里称为 DataSource Resource。业务通过 JDBC 标准接口访问数据库资源时,Seata 框架会对所有请求进行拦截,做一些操作。
每个本地事务提交时,Seata RM(Resource Manager,资源管理器) 都会向 TC(Transaction Coordinator,事务协调器) 注册一个分支事务。当请求链路调用完成后,发起方通知 TC 提交或回滚分布式事务,进入二阶段调用流程。此时,TC 会根据之前注册的分支事务回调到对应参与者去执行对应资源的第二阶段。
TC 是怎么找到分支事务与资源的对应关系呢?每个资源都有一个全局唯一的资源 ID,并且在初始化时用该 ID 向 TC 注册资源。在运行时,每个分支事务的注册都会带上其资源 ID。这样 TC 就能在二阶段调用时正确找到对应的资源。这就是我们的 AT 模式。简单总结一下,就是把每个数据库当做一个 Resource,在本地事务提交时会去注册一个分支事务。
AT模式是一种无侵入的分布式事务解决方案。在AT模式下,用户只需关注自己的"业务SQL",用户的"业务SQL"作为第一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作。
![ac9919e1c0f3baac380e4d702d7f3262.png](https://i-blog.csdnimg.cn/blog_migrate/1576a2de5701e417f3b77c0307372ce2.png)
AT模式如何做到对业务的无侵入
一阶段:
在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
![56cc0626b08c2137c941f213a9314755.png](https://i-blog.csdnimg.cn/blog_migrate/a723b0517f7ff5a43c533a40b3ad2c82.png)
二阶段提交:
二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
![80d5b2fc863b17a9c92df128f4d1ed2f.png](https://i-blog.csdnimg.cn/blog_migrate/804ce838ba0f390322aa7647fde5f17d.png)
二阶段回滚:
二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
![dbc8c1da8c0e34b830e4e0297c5450fa.png](https://i-blog.csdnimg.cn/blog_migrate/b89702101bf3494eae5631db6590a359.png)
AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。
当然官网对AT模式也进行了细致的讲解, 大家可以看下Seata官网的Seata AT模式。
Dubbo + Seata 实战案例
环境准备
Dubbo
docker-compose.yaml:
version: '3'
services:
zookeeper:
image: zookeeper
ports:
- 2181:2181
admin:
image: apache/dubbo-admin
depends_on:
- zookeeper
ports:
- 8080:8080
environment:
- admin.registry.address=zookeeper://zookeeper:2181
- admin.config-center=zookeeper://zookeeper:2181
- admin.metadata-report.address=zookeeper://zookeeper:2181
docker-compose.yaml:
version: "3"
services:
seata-server:
image: seataio/seata-server
hostname: seata-server
ports:
- "8091:8091"
environment:
- SEATA_PORT=8091
- STORE_MODE=file
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
目录结构
![bd288fc32f4f698859296c1d46deac21.png](https://i-blog.csdnimg.cn/blog_migrate/02bb01a5e89b618ddab2f3a0c493400e.png)
Storage : 商品库存逻辑模块;
Account: 用户账户逻辑模块;
Order: 商品订单逻辑模块;
Business: 业务层逻辑模块;
下面我通过Storage模块来描述Dubbo + Seata的接入,其他模块,例如account
, order
模块的接入都是相同的。
Storage商品库存模块
项目目录
.
├── java
│ └── cn
│ └── mushuwei
│ └── storage
│ ├── SeataStorageApplication.java #应用SpringBoot启动类
│ ├── api
│ │ ├── StorageApi.java #库存调用Dubbo接口
│ │ └── dto
│ │ └── CommodityDTO.java #库存数据传输类
│ ├── config
│ │ └── SeataAutoConfig.java #Seata配置类
│ ├── dao
│ │ └── StorageDao.java #库存持久化类
│ ├── entity
│ │ └── StorageDO.java #库存持久化实体
│ ├── provider
│ │ └── StorageApiImpl.java #库存调用Dubbo接口实现类
│ └── service
│ ├── StorageService.java #库存业务操作逻辑类
│ └── impl
│ └── StorageServiceImpl.java #库存业务操作逻辑实现类
└── resources
├── application.yml #应用配置文件
├── mybatis
│ └── storage.xml #mybatis xml文件
└── sql
└── storage.sql #数据库表结构和初始化数据
15 directories, 12 files
Pom.xml
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>log4j-over-slf4jartifactId>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>jul-to-slf4jartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>io.seatagroupId>
<artifactId>seata-spring-boot-starterartifactId>
dependency>
应用配置文件
# dubbo配置项,对应DubboConfigurationProperties 配置类
dubbo:
application: