2.springcloudAlibaba-openFeign
3.springcloudAlibaba-负载均衡器Ribbon
4.springcloudAlibaba-nacos配置中心
5.springcloudAlibaba-Seata 分布式事务
7.springcloudAlibaba-网关gateway
目录
事务
事务(Transaction) 是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据
库中,一个事务由一组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。
这四个属性通常称为ACID特性。
原子性(atomicity):个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么
都不做。
一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务
的中间状态不能被观察到的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数
据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性又分为四个级别:读
未提交(read uncommitted)、读已提交(read committed,解决脏读)、可重复读(repeatable
read,解决虚读)、串行化(serializable,解决幻读)。
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库
中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
任何事务机制在实现时,都应该考虑事务的ACID特性,包括:本地事务、分布式事务,及时不能
都很好的满足,也要考虑支持到什么程度
通常本地事务@Transational 基本就足够了,但是在分布式环境中 同一个方法中 可能调用不同的数据库或者调用不同的服务 服务再调用数据库 @Transational 明显就不能保证
Seata是什么
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)
官网:Seata
官方Demo: GitHub - seata/seata-samples: seata-samples
常见分布式事务解决方案
1、seata 阿里分布式事务框架
2、消息队列
3、saga
4、XA
他们有一个共同点,都是“两阶段(2PC)”。“两阶段”是指完成整个分布式事务,划分成两个步骤
完成。
实际上,这四种常见的分布式事务解决方案,分别对应着分布式事务的四种模式:AT、TCC、Saga、XA;
四种分布式事务模式,都有各自的理论基础,分别在不同的时间被提出;每种模式都有它的适用场
景,同样每个模式也都诞生有各自的代表产品;而这些代表产品,可能就是我们常见的(全局事务、
基于可靠消息、最大努力通知、TCC)
分布式事务理论基础
解决分布式事务,也有相应的规范和协议。分布式事务相关的协议有2PC、3PC。
由于三阶段提交协议3PC非常难实现,目前市面主流的分布式事务解决方案都是2PC协议
2PC两阶段提交协议:Prepare 和 Comm
AT模式(auto transcation)
主要是在项目中引入了 中间协调人
prepare:预执行
通过协调者向各个服务发送事务请求,查看是否可用执行,各个服务接收到请求后 执行对应操作 并将状态修改信息保存日志中 将相应结果返回给 协调者
在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数
据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,
在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库
事务内完成,这样保证了一阶段操作的原子性
Commit:执行事务提交
执行事务提交分为两种情况,正常提交和回退
二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一
阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方
式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务
数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一
致就说明有脏写,出现脏写就需要转人工处理。
在执行 Prepare 步骤过程中,如果某些参与者执行事务失败、宕机或与协调者之间的网络中断,那么协调者就无法收到所有参与者的 YES 响应,或者某个参与者返回了 No 响应,此时,协调者就会进入回退流程,对事务进行回退。
AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能
轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案
TCC 模式
1. 侵入性比较强, 并且得自己实现相关事务控制逻辑
2.在整个过程基本没有锁,性能更强
TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶
段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法
Seata搭建
Seata的三大角色
在 Seata 的架构中,一共有三个角色:
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
Seata Server(TC)环境搭建
配置server
seata 的环境搭建很麻烦,但是一旦搭建起来后面使用就是一个注解的事,所以这部分搭建要有耐心
下载安装包 根据版本对应 选择1.3.0 的版本
下载seata-server-1.3.0.tar.gz 解压 tar -zxvf
打开config/file.conf
修改mode="db"
修改数据库连接信息
这里file 不配也可以,后面配置到nacos config里
修改seata/conf 下registry.conf 配置nacos信息 一个注册 一个config
创建数据库seata
新建表:
下载code找到db 创建数据库 seata 这里我用的mysql 执行mysql.sql 创建三张表
配置nacos config
在下载的code中找到 script ,将script放到seata目录下
修改seata/script/config-center 下config.txt
找到seata/script/config-center/nacos 下nacos-config.sh 执行
sh nacos-config.sh -h nacosip -p 8848 -g SEATA_GROUP -t 命名空间 -u username -w password
登录nacos 控制台 看到多了很多seata配置文件证明执行成功
启动Seata Server
然后启动seata
cd /usr/local/seata/bin sh seata-server.sh -h 49.232.193.91 -p 8091 -m db
-h: 注册到注册中心的ip 这里注意如果 项目不在一个服务器 尽量使用外网ip 否则服务端可能会访问不到
-p: Server rpc 监听端口
-m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis)
-n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
-e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
如果要搭建集群的话
在nacos 服务列表可以看到 有seata-server 服务
配置客户端
新建两个个springcloud module seata-order /seata-store
配置 数据库 datasource nacos openFeign 这里就不细说了
创建两个表 order /store num 都给 10
然后在两个module中简单写两个sql
在seata-order service 中 先调用本地sql 再通过openFeign调用seata-store 中的sql
@Service public class OrderSeataService { @Resource(name = "daoSupport") private DaoSupport dao; @Autowired StoreSeataService storeSeataService; public void updateOrder() throws Exception { dao.update("OrderMapper.updateOrder", null); } @Transactional public void updateOrderNum() throws Exception { updateOrder(); storeSeataService.updateOrderNum(); int i=1/0; } }
/** * @author LZQ * @create 2022-07-31 8:57 * @desc **/ @Service @FeignClient(value = "seata-store", path = "/seataStore") public interface StoreSeataService { @RequestMapping("/updateStoreNum") String updateOrderNum(); }
@RestController @RequestMapping(value = "/seataStore") public class SeataStoreController { //@Autowired //OrderService orderService; @Autowired StoreSeataService storeService; @RequestMapping(value = "/updateStoreNum") public void getUserInfo() throws Exception { storeService.updateOrder(); // System.out.println("Seata全局事务id=================>{}"+ RootContext.getXID()); } }
在OrderSeataService 中updateOrderNum 方法上加入@Transactional 注解
调用此方法发现
本地事务回滚了,但是seata-store 并没有,所以@Transactional 注解 只能进行本地的回滚
配置客户端seata
在数据库中增加 事务的日志表 如果是多个数据库 在服务的数据库中都要加
-- 注意此处0.7.0+ 增加字段 context 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, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
增加seata依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
增加配置文件
cloud: alibaba: seata: tx-service-group: default_tx_group seata: registry: # 配置seata的注册中心,告诉seata clent 怎么去访问server type: nacos nacos: application: seata-server server-addr: 49.232.193.91:8848 username: nacos password: nacos group: SEATA_GROUP #seata 默认分组 namespace: 40733d09-019d-4d59-9ab0-823f3058beab config: type: nacos nacos: server-addr: 49.232.193.91:8848 username: nacos password: nacos group: SEATA_GROUP namespace: 40733d09-019d-4d59-9ab0-823f3058beab
其中 tx-service-group: default_tx_group 是上传配置文件中 事务服务分组 很多配置失败都是这里没写正确
总的配置文件
server: port: 6666 spring: application: name: seata-order cloud: nacos: server-addr: 49.232.193.91:8848 discovery: username: nacos password: nacos namespace: 40733d09-019d-4d59-9ab0-823f3058beab alibaba: seata: tx-service-group: default_tx_group datasource: driver-class-name: com.mysql.cj.jdbc.Driver name: defaultDataSource url: jdbc:mysql://49.232.193.91:3306/seata?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false username: seata password: seata ribbon: eureka: enabled: false seata: registry: # 配置seata的注册中心,告诉seata clent 怎么去访问server type: nacos nacos: application: seata-server server-addr: 49.232.193.91:8848 username: nacos password: nacos group: SEATA_GROUP namespace: 40733d09-019d-4d59-9ab0-823f3058beab config: type: nacos nacos: server-addr: 49.232.193.91:8848 username: nacos password: nacos group: SEATA_GROUP namespace: 40733d09-019d-4d59-9ab0-823f3058beab
seata-store 跟seata-order 都要配
配置完成后在方法上加入@GlobalTransactional 注解
// @Transactional @GlobalTransactional public void updateOrderNum() throws Exception { updateOrder(); storeSeataService.updateOrderNum(); int i=1/0; }
然后重新调用方法
可以看到数据库都回滚了 然后再看日志 seata 确实执行了回滚