## :-: springcloud 整合Seata实现分布式事务
## Seata简介 [https://github.com/seata](https://github.com/seata)
Seata(Simple Extensible Autonomous Transaction Architecture) 是 阿里巴巴开源的分布式事务中间件,以高效并且对业务 0 侵入的方式,解决微服务场景下面临的分布式事务问题。
## Seata解决方案
先介绍 `Seata` 分布式事务的几种角色:
* `Transaction Coordinator(TC)`: 全局事务协调者,用来协调全局事务和各个分支事务(不同服务)的状态, 驱动全局事务和各个分支事务的回滚或提交。
* `Transaction Manager™`: 事务管理者,业务层中用来开启/提交/回滚一个整体事务(在调用服务的方法中用注解开启事务)。
* `Resource Manager(RM)`: 资源管理者,一般指业务数据库代表了一个分支事务(`Branch Transaction`),管理分支事务与 `TC` 进行协调注册分支事务并且汇报分支事务的状态,驱动分支事务的提交或回滚。
典型的分布式事务周期包括以下步骤:
* TM向TC请求开启一个全局事务,TC给TM返回一个全局事务的XID;
* XID在微服务调用链之间传递;
* RM向TC注册XID下的分支事务;
* TM根据XID向TC发出提交或者回滚的请求;
* TC根据XID使RM提交或者回滚。
Seata 也是从两段提交演变而来的一种分布式事务解决方案,提供了 AT、TCC、SAGA 和 XA 等事务模式,这里重点介绍 AT模式。
#### AT模式
1、基于支持本地 ACID 事务的关系型数据库,整体机制是2PC的演变。
~~~
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,成功则批量地删除相应回滚日志记录,回滚则通过回滚日志进行反向补偿。
~~~
2、写隔离。
~~~
1)一阶段本地事务提交前,需要确保先拿到全局锁。
2)拿不到全局锁,不能提交本地事务;拿到全局锁,提交本地事务并插入undo_log记录。
3)拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并根据undo_log记录回滚本地事务,释放本地锁。
~~~
3、读隔离。
~~~
1)在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)
的默认全局隔离级别是 读未提交(Read Uncommitted)。
理解:在全局事务提交之前,本地事务会先提交,这时候查询数据,对于本地库是Read Committed,
对于全局来说是 Read Uncommitted。
2)如果要求全局的读已提交,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
~~~
## **springcloud 整合Seata实现分布式事务**
项目使用的是SpringBoot 2.x + SpringCloud,注册中心使用的是nacos
### **一、seata-server搭建**
1、下载最新版seata-server-1.4.0.zip
2、修改registry.conf。
registry.type 改成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 = ""
password = ""
}
```
3、使用file.conf,store.mode选择db,修改连接的数据库属性,其他的默认即可。
```
db {
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://*************.mysql.rds.aliyuncs.com:3306/hj_seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&zeroDateTimeBehavior=convertToNull&useSSL=false"
user = "root"
password = "****"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
```
4、在数据库中加入三个表。
```
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`lock_key` varchar(128) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint(4) DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(64) DEFAULT NULL,
`transaction_service_group` varchar(64) DEFAULT NULL,
`transaction_name` varchar(64) DEFAULT NULL,
`timeout` int(11) DEFAULT NULL,
`begin_time` bigint(20) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(96) DEFAULT NULL,
`transaction_id` mediumtext,
`branch_id` mediumtext,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(32) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
5、在bin目录下,启动server服务,默认是8091端口。
### **二、springCloud 项目整合**
**Springboot:2.3.2.RELEASE**
**springCloud alibaba:2.2.0.RELEASE**
**springCloud :Hoxton.RELEASE**
**搭建2个微服务项目hj-seata-client1 hj-seata-client2 调用关系为 hj-seata-client调用hj-seata-client1的服务**
**1、配置**
~~~
<dependencies>
<dependency>
<groupId>com.hjf.mall</groupId>
<artifactId>hj-common</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<!--mysql: 数据库链接驱动工具 8.0版本可以对应数据库5.6、5.7、8.0 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--java工具类 Bean注解简化开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- mybatis-plus start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<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.0</version>-->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--cloud依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
~~~
**application.yml**
*****
~~~
server:
port: 6002
spring:
main:
allow-bean-definition-overriding: true
application:
name: seata-B
# --------------cloud配置------------------
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #注册服务控制中心
# 这里配置分组
alibaba:
seata:
tx-service-group: my_test_tx_group
#------------数据库链接-----------------------------
datasource:
url: jdbc:mysql://****.mysql.rds.aliyuncs.com:3306/hj_mall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: ***
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.hjf.test.entity,com.hjf.base,MyOgnl
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: false
global-config:
db-config:
id-type: auto
#开启hystrix 熔断
feign:
hystrix:
enabled: true
client:
config:
default:
connect-timeout: 4000
read-timeout: 4000
# --------------负载均衡器配置------------------
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
#阿里分布式事务配置
seata:
service:
tx-service-group: my_test_tx_group
vgroup-mapping:
#这里的组名my_test_tx_group就是上面已经配置过的
# seata-server 对应的就是register.conf里的application选项的内容
my_test_tx_group: seata-server
grouplist:
#这里对应的就是上面的seata-server,后面的蚕食seata服务的IP地址和端口号
seata-server: 127.0.0.1:8091
enable-degrade: false
disable-global-transaction: false
~~~
*****
**2、配置数据源代理和扫描器**
~~~
@Configuration
public class DataSourceProxyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
}
~~~
**3、在需要开启分布式事务的方法上加上@GlobalTransactional注解。**
~~~
在TM开启全局事务之后,TC返回一个XID,RootContext类中会保存XID,该XID是一个线程变量。
RM中根据XID向TC注册分支变量。
~~~
*****
~~~
@GlobalTransactional(rollbackFor = Exception.class)
public BaseResp addCarousel(MarketCarousel q) {
marketCarouselMapper.insert(q);
//远程调用微服务添加数据
BaseResp r=feignService.add(q);
if (StringUtils.isBlank(q.getTitle())){
//抛出异常事务回滚
throw new NullPointerException("抛出异常........");
}
return BaseResp.SUCCESS;
}
~~~
*****
**4、在业务系统和各个微服务中加入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;
~~~
- 项目介绍
- 项目声明
- 项目简介
- 架构设计
- 项目亮点功能介绍
- 技术栈介绍
- 核心功能
- 运行环境
- 项目更新日志
- 文档更新日志
- F&Q
- 部署教程
- 环境准备
- JDK安装
- JDK1.8,17共存
- maven
- 分布式缓存Redis
- 单机版
- 集群
- 注册&配置中心alibaba/nacos
- 介绍
- Nacos安装
- Nacos配置中心
- Nacos注册发现
- Nacos生产部署方案
- 服务监控-BootAdmin
- 基本介绍
- 如何使用
- 整合Admin-Ui
- 客户端配置
- 链路追踪
- 基本介绍
- SkyWalking-1
- Skywalking-1
- 消息队列
- Kafka
- docker安装kafka
- Linux集群
- Maven私服
- nexus安装部署
- nexus使用介绍
- 全文搜索elasticsearch
- windows集群搭建
- docker安装es
- ElasticHD
- linux集群部署
- 统一日志解决方案
- 日志解决方案设计
- 介绍与相关资料
- ELK安装部署
- elasticsearch 7.5
- logstash-7.5
- kibana-7.5
- filebeat
- 服务监控-Prometheus
- Prometheus安装配置
- Prometheus介绍
- grafana
- 持续集成部署CICD
- 自动化部署Jenkins
- 安装部署win
- 打包发布远程执行
- 安装部署linux
- jenkins+gitlab+docker容器化工程自动化部署
- Git
- CICD说明
- 阿里云效
- CentOS_MYSQL安装
- docker
- 安装
- Docker安装Nginx
- Docker部署启动springboot
- dockerCompose
- harbor
- Docker私有镜像仓库
- Portainer
- Docker远程连接设置
- 打包工程
- 必要启动模块
- 核心模块
- 登录认证
- 缓存功能
- 日志模块
- 分布式锁
- 消息队列
- 异常处理
- 系统接口
- 参数验证
- es检索
- 数据导出
- 系统设计
- 系统总体架构
- 扩展模块(可选)
- 限流熔断alibaba/sentinel
- 使用Sentinel实现gateway网关及服务接口限流
- Sentinel使用Nacos存储规则及同步
- 服务调用Feign
- Feign基本介绍
- 如何使用
- 负载均衡
- 请求超时
- 请求拦截器
- 分布式任务调度
- XXL-JOB
- 分布式事务
- TX-LCN
- Seata
- Seata原理解析
- 数据库分库分表
- swagger文档
- 分布式ID生成器解决方案
- 服务网关CloudGateway
- 基本介绍
- 使用网关
- 路由配置
- 全局过滤器
- 服务认证授权架构设计
- 认证服务流程
- 授权服务流程
- 系统幂等性设计与实践
- 分布式日志链路跟踪
- 实时搜索系统设计
- 应用性能
- 压力测试工具
- Apache JMeter介绍和安装
- ApacheJMeter使用
- JVM
- JVM性能调优
- 常见JVM内存错误及解决方案
- JVM 分析工具详解
- Spring Cloud性能调优
- Linux运维
- Linux 常用命令
- Linux开启端口