阶段八:服务框架高级(第二章:分布式事务)
阶段八:服务框架高级(第二章:分布式事务)
- Day-分布式事务
- 0.学习目标
- 1.分布式事务问题
- 1.1.本地事务
- 1.2.分布式事务
- 1.3.演示分布式事务问题
- 2.理论基础
- 2.1.CAP定理
- 2.1.1.一致性
- 2.1.2.可用性
- 2.1.3.分区容错
- 2.1.4.矛盾
- 2.2.BASE理论
- 2.3.解决分布式事务的思路
- 3.初识`Seata`
- 3.1.Seata的架构 【==理解==】
- 3.2.部署TC服务 【==重要,不需要背过,参照文档就可以==】
- 3.3.微服务集成`Seata` 【==重要==】
- 3.3.1.引入依赖
- 3.3.2.配置TC地址
- 3.3.3.其它服务
- 3.3.4.小结
- 4.动手实践
- 4.1.XA模式 【==重要,用的少==】
- 4.1.1.两阶段提交
- 4.1.2.Seata的XA模型 【==理解即可==】
- 4.1.3.优缺点
- 4.1.4.实现`XA`模式 【==重要==】
- 4.2.AT模式 【==重要,重要==】
- 4.2.1.Seata的AT模型 【==理解==】
- 4.2.2.流程梳理
- 4.2.3.AT与XA的区别
- 4.2.4.脏写问题 【==理解==】
- 4.2.5.优缺点
- 4.2.6.实现AT模式 【==重要==】
- 4.3.TCC模式 【==重要,重要==】
- 4.3.1.流程分析 【==理解==】
- 4.3.2.Seata的TCC模型 【==理解==】
- 4.3.3.优缺点
- 4.3.4.事务悬挂和空回滚 【==重要==】
- 1)空回滚
- 2)业务悬挂
- 4.3.5.实现TCC模式 【==重要==】
- 1)思路分析
- 2)声明TCC接口
- 2)编写实体类
- 2)编写`mapper`操作数据表
- 3)编写实现类 (不全,需要看代码补全)
- 4.4.SAGA模式 【没怎么说】
- 4.4.1.原理
- 4.4.2.优缺点
- 4.5.四种模式对比
- 5.高可用 【==重要==】
- 5.1.高可用架构模型
- 5.2.实现高可用
Day-分布式事务
0.学习目标
1.分布式事务问题
1.1.本地事务
本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则:
1.2.分布式事务
分布式事务:在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务,例如:
- 跨数据源的分布式事务
- 跨服务的分布式事务
- 综合情况
在数据库水平拆分、服务垂直拆分之后,一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例,包括下面几个行为:
- 创建新订单
- 扣减商品库存
- 从用户账户余额扣除金额
完成上面的操作需要访问三个不同的微服务和三个不同的数据库。
订单的创建、库存的扣减、账户扣款在每一个服务和数据库内是一个本地事务,可以保证ACID原则。
但是当我们把三件事情看做一个"业务",要满足保证“业务”的原子性,要么所有操作全部成功,要么全部失败,不允许出现部分成功部分失败的现象,这就是分布式系统下的事务了。
此时ACID难以满足,这是分布式事务要解决的问题
1.3.演示分布式事务问题
我们通过一个案例来演示分布式事务的问题:
1)创建数据库,名为seata_demo
,然后导入课前资料提供的SQL
文件:
2)导入课前资料提供的微服务:
微服务结构如下:
其中:
seata-demo
:父工程,负责管理项目依赖
account-service
:账户服务,负责管理用户的资金账户。提供扣减余额的接口storage-service
:库存服务,负责管理商品库存。提供扣减库存的接口order-service
:订单服务,负责管理订单。创建订单时,需要调用account-service
和storage-service
3)启动nacos
、所有微服务
4)测试下单功能,发出Post
请求:
请求如下:
curl --location --request POST 'http://localhost:8082/order?userId=user202103032042012&commodityCode=100202003032041&count=20&money=200'
如图:
测试发现,当库存不足时,如果余额已经扣减,并不会回滚,出现了分布式事务问题。
2.理论基础
解决分布式事务问题,需要一些分布式系统的基础知识作为理论指导。
2.1.CAP定理
1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标。
- Consistency(一致性)
- Availability(可用性)
- Partition tolerance (分区容错性)
它们的第一个字母分别是 C、A、P。
Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。
2.1.1.一致性
Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致。
比如现在包含两个节点,其中的初始数据是一致的:
当我们修改其中一个节点的数据时,两者的数据产生了差异:
要想保住一致性,就必须实现node01
到 node02
的数据 同步:
2.1.2.可用性
Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝。
如图,有三个节点的集群,访问任何一个都可以及时得到响应:
当有部分节点因为网络故障或其它原因无法访问时,代表节点不可用:
2.1.3.分区容错
Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务
2.1.4.矛盾
在分布式系统中,系统间的网络不能100%保证健康,一定会有故障的时候,而服务有必须对外保证服务。因此Partition Tolerance【分区容错】不可避免。
当节点接收到新的数据变更时,就会出现问题了
如果此时要保证一致性,就必须等待网络恢复,完成数据同步后,整个集群才对外提供服务,服务处于阻塞状态,不可用。
如果此时要保证可用性,就不能等待网络恢复,那node01、node02与node03之间就会出现数据不一致。
也就是说,在P一定会出现的情况下,A和C之间只能实现一个。
2.2.BASE理论
BASE
理论是对CAP
的一种解决思路,包含三个思想:
Basically Available
(基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。Soft State
(软状态): 在一定时间内,允许出现中间状态,比如临时的不一致状态。Eventually Consistent
(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
2.3.解决分布式事务的思路
分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP
定理和BASE
理论,有两种解决思路:
-
AP模式
:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。 -
CP模式
:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。
但不管是哪一种模式,都需要在子系统事务之间互相通讯,协调事务状态,也就是需要一个 事务协调者(TC):
这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务。
3.初识Seata
Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。
3.1.Seata的架构 【理解】
Seata事务管理中有三个重要的角色:
-
TC (Transaction Coordinator) - 事务协调者: 维护全局和分支事务的状态,协调全局事务提交或回滚。
-
TM (Transaction Manager) - 事务管理器: 定义全局事务的范围、开始全局事务、提交或回滚全局事务。
-
RM (Resource Manager) - 资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚 。
整体的架构如图:
Seata
基于上述架构提供了四种不同的分布式事务解决方案:
XA模式
:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入TCC模式
:最终一致的分阶段事务模式,有业务侵入AT模式
:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式SAGA模式
:长事务模式,有业务侵入
无论哪种方案,都离不开TC
,也就是事务的协调者。
3.2.部署TC服务 【重要,不需要背过,参照文档就可以】
参考课前资料提供的文档《 seata的部署和集成.md 》:
https://editor.csdn.net/md?articleId=128410730
3.3.微服务集成Seata
【重要】
我们以order-service
为例来演示。
实际中每个微服务都要集成一遍Seata
,参与全局事务的每个微服务都要做这个动作;
3.3.1.引入依赖
首先,在order-service
中引入依赖:
<!--seata-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions> <!--代表排除,也要写--><!--版本较低,1.3.0,因此排除--> <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><!--seata starter 采用1.4.2版本--><version>${seata.version}</version> <!--在父工程中有定义版本,也可以直接写版本号-->
</dependency>
3.3.2.配置TC地址
在order-service
中的application.yml
中,配置TC
服务信息,通过注册中心nacos
,结合服务名称获取TC
地址:
下面的是要根据自己的实际来更改的
group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP ,与nacos浏览器显示的是一样的application: seata-tc-server # seata服务名称 ,与部署TC服务时是一样的seata-demo: SH #将seata-demo映射到SH集群
seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址# 参考tc服务自己的registry.conf中的配置;# 包括:地址、namespace、group、application-name 、clustertype: nacos # 注册中心类型 nacosnacos: # tcserver-addr: 127.0.0.1:8848 # nacos地址namespace: "" # namespace,默认为空,空就是publicgroup: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP ,与nacos浏览器显示的是一样的application: seata-tc-server # seata服务名称 ,与部署TC服务时是一样的username: nacospassword: nacostx-service-group: seata-demo # 事务组名称;根据这个获取tc服务的cluster(集群)名称service:vgroup-mapping: # 事务组与cluster的映射关系seata-demo: SH #将seata-demo映射到SH集群
微服务如何根据这些配置寻找TC的地址呢?
我们知道注册到Nacos
中的微服务,确定一个具体实例需要四个信息:
namespace
:命名空间group
:分组application
:服务名cluster
:集群名
以上四个信息,在刚才的yaml文件中都能找到:
namespace
为空,就是默认的public
结合起来,TC服务的信息就是:public@DEFAULT_GROUP@seata-tc-server@SH
,这样就能确定TC服务集群了。然后就可以去Nacos
拉取对应的实例信息了。
3.3.3.其它服务
其它两个微服务也都参考order-service
的步骤来做,完全一样。
3.3.4.小结
4.动手实践
下面我们就一起学习下Seata中的四种不同的事务模式。
4.1.XA模式 【重要,用的少】
XA
规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
4.1.1.两阶段提交
XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交。
正常情况:
异常情况:
一阶段:
- 事务协调者通知每个事物参与者执行本地事务
- 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
二阶段:
- 事务协调者基于一阶段的报告来判断下一步操作
- 如果一阶段都成功,则通知所有事务参与者,提交事务
- 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
4.1.2.Seata的XA模型 【理解即可】
Seata
对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:
RM
一阶段的工作:
① 注册分支事务到TC
② 执行分支业务sql但不提交
③ 报告执行状态到TC
TC
二阶段的工作:
- TC检测各分支事务执行状态
a.如果都成功,通知所有RM提交事务
b.如果有失败,通知所有RM回滚事务
RM
二阶段的工作:
- 接收TC指令,提交或回滚事务
4.1.3.优缺点
XA
模式的优点是什么?
- 事务的强一致性,满足ACID原则。
- 常用数据库都支持,实现简单,并且没有代码侵入
XA
模式的缺点是什么?
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差(低可用性)
- 依赖关系型数据库实现事务(其他数据库有事就不能用了)
4.1.4.实现XA
模式 【重要】
Seata
的starter
已经完成了XA
模式的自动装配,实现非常简单,步骤如下:
1)修改application.yml
文件(每个参与事务的微服务),开启XA模式:
seata:data-source-proxy-mode: XA
2)给发起全局事务的入口方法添加@GlobalTransactional
注解:
本例中是OrderServiceImpl
中的create
方法.
3)重启服务并测试
重启order-service
等每个微服务,再次测试,发现无论怎样,三个微服务都能成功回滚。
4.2.AT模式 【重要,重要】
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷。
4.2.1.Seata的AT模型 【理解】
基本流程图:
阶段一RM
的工作:
- 注册分支事务
- 记录
undo-log
(数据快照) - 执行业务sql并提交
- 报告事务状态
阶段二提交时RM的工作:
- 删除
undo-log
即可
阶段二回滚时RM
的工作:
- 根据
undo-log
恢复数据到更新前
4.2.2.流程梳理
我们用一个真实的业务来梳理下AT模式的原理。
比如,现在又一个数据库表,记录用户余额:
id | money |
---|---|
1 | 100 |
其中一个分支业务要执行的SQL
为:
update tb_account set money = money - 10 where id = 1
AT模式下,当前分支事务执行流程如下:
一阶段:
1)TM发起并注册全局事务到TC
2)TM调用分支事务
3)分支事务准备执行业务SQL
4)RM拦截业务SQL,根据where条件查询原始数据,形成快照。
{"id": 1, "money": 100
}
5)RM执行业务SQL,提交本地事务,释放数据库锁。此时 money = 90
6)RM报告本地事务状态给TC
二阶段:
1)TM通知TC事务结束
2)TC检查分支事务状态
a)如果都成功,则立即删除快照
b)如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}
),将快照恢复到数据库。此时数据库再次恢复为100
流程图:
4.2.3.AT与XA的区别
简述AT
模式与XA
模式最大的区别是什么?
- XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
- XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
- XA模式强一致;AT模式最终一致
4.2.4.脏写问题 【理解】
在多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图:
解决思路就是引入了全局锁的概念。在释放DB锁
(数据库锁)之前,先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。
实际还可能存在见缝插针的问题:
解决办法:使用了两个快照:更新前的快照、更新后的快照,如果出现问题就会申请人工介入;
4.2.5.优缺点
AT模式的优点:
- 一阶段完成直接提交事务,释放数据库资源,性能比较好
- 利用全局锁实现读写隔离
- 没有代码侵入,框架自动完成回滚和提交
AT模式的缺点:
- 两阶段之间属于软状态,属于最终一致
- 框架的快照功能会影响性能,但比XA模式要好很多
4.2.6.实现AT模式 【重要】
AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。
只不过,AT模式需要一个表来记录全局锁、另一张表来记录数据快照undo_log。
【需要两张表分别记录全局锁和数据快照,这两张表分别在不同的数据库中】
1)导入数据库表,记录全局锁
导入课前资料提供的Sql文件:seata-at.sql
,其中 lock_table
(全局锁)导入到TC服务关联的数据库seata
, undo_log
(快照)表导入到微服务关联的数据库seata_demo
:
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (`row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`branch_id` bigint(20) NOT NULL,`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime NULL DEFAULT NULL,`gmt_modified` datetime NULL DEFAULT NULL,PRIMARY KEY (`row_key`) USING BTREE,INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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 INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
2)修改application.yml
文件(每个参与事务的微服务),将事务模式修改为AT模式即可:
seata:data-source-proxy-mode: AT # 默认就是AT
3)重启所有有关的微服务并测试
重启order-service
等每个微服务,再次测试,发现无论怎样,三个微服务都能成功回滚。
4.3.TCC模式 【重要,重要】
注意: 并不是所有的微服务都适合使用TCC模式,可以一部分使用AT一部分使用TCC;AT和TCC可以混用;
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:
Try
:资源的检测和预留;Confirm
:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。Cancel
:预留资源释放,可以理解为try的反向操作。
4.3.1.流程分析 【理解】
举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。
-
阶段一( Try ):检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30
初识余额:
余额充足,可以冻结:
此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变。事务直接提交无需等待其它事务。 -
阶段二(Confirm):假如要提交(Confirm),则冻结金额扣减30
确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好啦:
此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70元 -
阶段二(Canncel):如果要回滚(Cancel),则冻结金额扣减30,可用余额增加30
需要回滚,那么就要释放冻结金额,恢复可用金额:
4.3.2.Seata的TCC模型 【理解】
Seata 中的 TCC模型 依然延续之前的事务架构,如图:
4.3.3.优缺点
TCC模式的每个阶段是做什么的?
Try
:资源检查和预留Confirm
:业务执行和提交Cancel
:预留资源的释放
TCC的优点是什么?
- 一阶段完成直接提交事务,释放数据库资源,性能好
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
TCC的缺点是什么?【消耗人】
- 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理
4.3.4.事务悬挂和空回滚 【重要】
1)空回滚
当某分支事务的try
阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel
操作。在未执行try
操作时先执行了cancel
操作,这时cancel
不能做回滚,就是空回滚。
如图:
执行cancel
操作时,应当判断try
是否已经执行,如果尚未执行,则应该空回滚。
2)业务悬挂
对于已经空回滚的业务,之前被阻塞的try操作恢复(但是它恢复的太迟了,整个全局事务已经结束了),继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。
执行try
操作时,应当判断cancel
是否已经执行过了,如果已经执行,应当阻止空回滚后的try
操作,避免悬挂
4.3.5.实现TCC模式 【重要】
注意: 并不是所有的微服务都适合使用TCC模式,可以一部分使用AT一部分使用TCC;AT和TCC可以混用;
解决空回滚和业务悬挂问题,必须要记录当前事务状态,是在try
、还是cancel
?
1)思路分析
为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:
在微服务的数据库seata_demo
中执行创建
CREATE TABLE `account_freeze_tbl` (`xid` varchar(128) NOT NULL,`user_id` varchar(255) DEFAULT NULL COMMENT '用户id',`freeze_money` int(11) unsigned DEFAULT '0' COMMENT '冻结金额',`state` int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
其中:
xid
:是全局事务idfreeze_money
:用来记录用户冻结金额state
:用来记录事务状态
那此时,我们的业务该怎么做呢?
- Try业务:
- 记录冻结金额和事务状态到
account_freeze
表 - 扣减
account
表可用金额
- 记录冻结金额和事务状态到
- Confirm业务
- 根据xid删除
account_freeze
表的冻结记录
- 根据xid删除
- Cancel业务
- 修改
account_freeze
表,冻结金额为0,state为2 - 修改
account
表,恢复可用金额
- 修改
- 如何判断是否空回滚?
cancel
业务中,根据xid查询account_freeze_tbl
表,如果为null则说明try还没做,需要空回滚
- 如何避免业务悬挂?
try
业务中,根据xid
查询account_freeze_tbl
表 ,如果已经存在则证明Cancel已经执行,拒绝执行try业务
接下来,我们改造account-service
,利用TCC
实现余额扣减功能。
2)声明TCC接口
TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明,
我们在account-service
(账户微服务)项目中的cn.itcast.account.service
包中新建一个接口,声明TCC三个接口:
package cn.itcast.account.service;@LocalTCC
public interface AccountTCCService {@TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")//给参数加注解(在deduct方法中@BusinessActionContextParameter)void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,@BusinessActionContextParameter(paramName = "money")int money);boolean confirm(BusinessActionContext ctx); //BusinessActionContext ct上下文对象,可以获取当前的事务信息和参数信息,前提是要给参数加注解(在deduct方法中@BusinessActionContextParameter)boolean cancel(BusinessActionContext ctx);//BusinessActionContext ct上下文对象,可以获取当前的事务信息和参数信息
}
2)编写实体类
账户实体类:
记录当前事务id和执行状态的实体类,对应上面创建的account_freeze_tbl
数据表
2)编写mapper
操作数据表
操作记录当前事务id和执行状态数据表,对应上面创建的account_freeze_tbl
数据表:
操作账户数据表:
3)编写实现类 (不全,需要看代码补全)
在account-service
服务中的cn.itcast.account.service.impl
包下新建一个类,实现TCC
业务:
package cn.itcast.account.service.impl;@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {@Autowiredprivate AccountMapper accountMapper;//下面Try方法要操作两张表,把两张表的mapper层的方法注入进来@Autowiredprivate AccountFreezeMapper freezeMapper; //下面Try方法要操作两张表,把两张表的mapper层的方法注入进来@Override@Transactional //下面一抛异常,走到这里回滚public void deduct(String userId, int money) { //Try方法// 0.获取事务idString xid = RootContext.getXID(); //拿到全局事务id// 1.扣减可用余额accountMapper.deduct(userId, money); //调用mapper层的操作数据库方法// 2.记录冻结金额,事务状态AccountFreeze freeze = new AccountFreeze(); //AccountFreeze的实体类;freeze.setUserId(userId);freeze.setFreezeMoney(money);freeze.setState(AccountFreeze.State.TRY);freeze.setXid(xid); //使用 // 0.获取事务idfreezeMapper.insert(freeze); //调用mapper层的操作数据库方法}@Overridepublic boolean confirm(BusinessActionContext ctx) {// 1.获取事务idString xid = ctx.getXid();// 2.根据id删除冻结记录int count = freezeMapper.deleteById(xid); //调用mapper层的操作数据库方法return count == 1;}@Overridepublic boolean cancel(BusinessActionContext ctx) {// 0.查询冻结记录String xid = ctx.getXid();AccountFreeze freeze = freezeMapper.selectById(xid); //得到freeze对象// 1.恢复可用余额//参数是用户id和金额accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());// 2.将冻结金额清零,状态改为CANCELfreeze.setFreezeMoney(0); //冻结金额清零freeze.setState(AccountFreeze.State.CANCEL); //修改状态为CANCELint count = freezeMapper.updateById(freeze); //调用mapper层的操作数据库方法return count == 1;}
}
4.4.SAGA模式 【没怎么说】
Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。
其理论基础是Hector & Kenneth 在1987年发表的论文Sagas。
Seata官网对于Saga的指南:https://seata.io/zh-cn/docs/user/saga.html
4.4.1.原理
在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。
Saga也分为两个阶段:
- 一阶段:直接提交本地事务
- 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚
4.4.2.优缺点
优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
- 不用编写TCC中的三个阶段,实现简单
缺点:
- 软状态持续时间不确定,时效性差
- 没有锁,没有事务隔离,会有脏写
4.5.四种模式对比
我们从以下几个方面来对比四种实现:
- 一致性:能否保证事务的一致性?强一致还是最终一致?
- 隔离性:事务之间的隔离性如何?
- 代码侵入:是否需要对业务代码改造?
- 性能:有无性能损耗?
- 场景:常见的业务场景
如图:
5.高可用 【重要】
Seata
的TC
服务作为分布式事务核心,一定要保证集群的高可用性。
5.1.高可用架构模型
搭建TC服务集群非常简单,启动多个TC服务,注册到nacos即可。
但集群并不能确保100%安全,万一集群所在机房故障怎么办?所以如果要求较高,一般都会做异地多机房容灾。
比如一个TC集群在上海,另一个TC集群在杭州:
微服务基于事务组(tx-service-group)与TC集群的映射关系,来查找当前应该使用哪个TC集群。当SH集群故障时,只需要将vgroup-mapping中的映射关系改成HZ。则所有微服务就会切换到HZ的TC集群了。
5.2.实现高可用
具体实现请参考课前资料提供的文档《seata的部署和集成.md》:
第三章节:
直接参考https://editor.csdn.net/md?articleId=128410730即可
【# 叁、TC服务的高可用和异地容灾】
相关文章:
阶段八:服务框架高级(第二章:分布式事务)
阶段八:服务框架高级(第二章:分布式事务)Day-分布式事务0.学习目标1.分布式事务问题1.1.本地事务1.2.分布式事务1.3.演示分布式事务问题2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾2.2.BASE理论2.3.解…...
RPC异步化原理
深入RPC,更好使用RPC,须从RPC框架整体性能考虑问题。得知道如何提升RPC框架的性能、稳定性、安全性、吞吐量及如何在分布式下快速定位问题。RPC框架如何压榨单机吞吐量? 1 前言 TPS一直上不去,压测时CPU压到40%~50%就…...
C# 多窗口切换的实现
1、目的在主窗口中根据不同的按钮选择不同的子窗口显示。2、实现(1)、创建Winform窗体程序,放入SplitContainer控件splitContainer1将窗体分成左右2部分;(2)、在左侧splitContainer1.panel1中放入3个Button…...
【深度学习】RNN
1. 什么是RNN 循环神经网络(Recurrent Neural Network, RNN)是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递…...
招聘岗位,机会难得
岗位需求 费话不多说,直接上JD: 嵌入式开发工程师: 17:411.计算机、通信等相关专业。 2.熟悉网络基础知识,熟悉802.11a/b/g/n/ac协议,能通过抓包等分析手段排查定位各种wifi相关问题。 3.熟悉路由器主要功能及实现原…...
web打印的几种方法(2023)
在工作中出现web打印的情况是非常多的,其实这也是一个比较烦人的问题,这篇博客整理一下关于Web打印的一些方法或者方式。 1. window.print() 这个方法是用来打印网页的,页面上的其他的元素也会被打印处理,在打印的时候页眉页脚是…...
代码随想录算法训练营day44 | 动态规划之完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ
day44完全背包基础知识问题描述举个栗子518. 零钱兑换 II1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组377. 组合总和 Ⅳ1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例来推导dp数组完全背包基…...
IntelliJ IDEA 实用插件推荐(包含使用教程)
IntelliJ IDEA 实用插件推荐 背景:电脑重装了,重新下载了最新版的IntelliJ IDEA,感觉默认模式有点枯燥,于是决定从网上下载一些实用美观的插件优化自己以后吃饭的工具,现在推荐的都是目前还能用的(亲身实践…...
WideDeep模型
google提出的Wide&deep模型,将线性模型与DNN很好的结合起来,在提高模型泛化能力的同时,兼顾模型的记忆性。wide&deep这种将线性模型与DNN的并行连接模式,后来称为推荐领域的经典模式,奠定了后面深度学习模型的…...
nacos集群模式+keepalived搭建高可用服务
实际工作中如果nacos这样的核心服务停掉了或者整个服务器宕机了,那整个系统也就gg了,所以像这样的核心服务我们必须要搞个3个或者3个以上的nacos集群部署,实现高可用; 部署高可用版本之前,首先你要会部署单机版的naco…...
吉利「银河」负重突围
吉利控股集团最新公布的数据显示,2022年,吉利控股集团汽车总销量超230万辆,同比增长4.3%。其中,新能源汽车销量超64万辆,同比增长100.3%。 在中国本土市场,2022年吉利集团旗下品牌乘用车总交付量为135.84万…...
QT之图形视图框架概述——Graphics View Framework
QT之图形视图框架概述——Graphics View Framework1. 概述2. 核心类3. 事件传递4. Graphics View 坐标系统5. 参考1. 概述 Graphics View Framework是子Qt 4.2引入的,用来取代之前版本中的QCanvas。Graphics View Framework提拱了用于大量2D图形项的管理和交互的能…...
【SQL开发实战技巧】系列(二十二):数仓报表场景(上) 从分析函数效率一定快吗聊一聊结果集分页和隔行抽样实现方式
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...
小米无线AR眼镜探索版细节汇总
在MWC 2023期间,小米正式发布了一款无线AR眼镜,虽然还没看过实机,但XDA提前上手体验,我们从中进行总结。首先我要说的是,小米这款眼镜和高通无线AR眼镜参考设计高度重叠,产品卖点几乎一致,只是增…...
Web3中文|Litra:简洁而优美的NFT流动性协议,能给NFT市场带来什么?
2021年,NFT元年2021年,无疑是 NFT 的“元年”。这一年推特创始人的首条推特被拍出250万美元,加密艺术家Beeple的数字作品“First 5000 Days”在佳士得以6900万美元价格成交,无聊猿最高上涨了1800倍。2021年11月,在Goog…...
SSL证书对虚拟主机的用处有哪些?
虚拟主机是指在同一台服务器上,通过不同的域名或IP地址为多个网站提供服务的一种网络主机。而SSL证书则是一种数字证书,它用于加密网站与用户之间的通信,确保数据传输的安全性和完整性。在虚拟主机上,SSL证书有以下几个用处&#…...
SpringCloud之MQ笔记分享
MQ异步通信 初始MQ 同步通信 优点:时效性较强,可以以及得到结果 Feign就属于同步方式–问题: 耦合问题性能下降(中间的等待时间)资源浪费级联失败 异步通信 优点 耦合度低性能提升,吞吐量高故障隔离…...
动态规划背包问题
背包问题的分类 拿到背包问题,最重要的是会归类到哪一种背包问题中,常见的考题里主要是01背包和完全背包,leetcode上连多重背包的题目都没有。实际完全背包问题就是01背包的一种。 对一和零这道题,很多人容易把m看成一个背包,n看成另一个背包,从而当做多重背包。然而这…...
OpenCV4.x图像处理实例-张嘴和闭嘴检测
张嘴和闭嘴检测 在活体验证中,张嘴和闭嘴检测也是一个重要的环节。本文将介绍如何通过检测人脸上唇和下唇的关键点,并计算上唇和下唇的关键点的距离来检测当前人脸状态是否处于张嘴或闭嘴。 张嘴和闭嘴检测主要步骤如下: 第一步,安装依赖库 示例中使用到OpenCV和MediaP…...
软考高级系统分析师系列论文之十二:论实时控制系统与企业信息系统集成在工业控制的常规应用
软考高级系统分析师系列论文之十二:论实时控制系统与企业信息系统集成在工业控制的常规应用 一、摘要二、正文三、总结一、摘要 本文通过“工控组态软件”项目的开发,着重讨论实时系统与信息系统的集成。近年来,国内外的组态软件取得了很大的发展,已广泛应用于企业生产。组…...
蓝桥杯入门即劝退(二十三)货物摆放问题
欢迎关注点赞评论,共同学习,共同进步! ------持续更新蓝桥杯入门系列算法实例-------- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章…...
经验之谈——指标异常了怎么办?
本文参考了数据万花筒的文章,结合我自己工作经验。希望给大家一些帮助。 指标异常排查,是数据分析师的工作重点之一,是各行各业数据分析师都绕不开的话题。 本文试图回答: 1、指标波动的影响因素有哪些? 2、如何快速…...
影视领域解说电影怎样做才会更加出彩?
还有没有想要做影视解说的新手朋友~给大家分享一下影视解说快速上手的软件工具! 一、解说文案 文案是影视解说中最重要的步骤,如果你无法保证文案足够优秀,那么请务必让所有语句通顺,整体通篇下来让人知道你是在讲一个完整的故事…...
【Spring6】| Spring对IoC的实现(核心重点)
目录 一:Spring对IoC的实现 1. IoC 控制反转 2. 依赖注入 2.1 set注入 2.2 构造注入 3. set注入专题 3.1 注入外部Bean 3.2 注入内部Bean 3.3 注入简单类型 3.4 级联属性赋值(了解) 3.5 注入数组 3.6 注入List集合和Set集合 3.7…...
部门来了个测试工程师,听说是00后,实在是太卷了.....
都说00后躺平了,但是有一说一,该卷的还是卷。 这不,前段时间我们部门来了个00后,工作没两年,跳槽到我们公司起薪18K,都快接近我了。后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了。…...
冲冲冲,力扣javascript刷题——数组总结
力扣javascript刷题——数组总结冲冲冲,力扣刷题——数组总结1.二分查找力扣704题:二分查找35.搜索插入位置34.在排序数组中查找元素的第一个和最后一个位置69.x 的平方根367. 有效的完全平方数2.双指针法27. 移除元素26. 删除有序数组中的重复项283.移动零844. 比较…...
使用kotlin编写html dsl框架
前排提醒,这个框架就是我写着玩的,如果您已经会使用vue或其他前端框架,这篇文章可能对您没有什么意义。即使您不会如上提到的框架,也不要对该框架报有过高的期待,该框架更多的是,我自己的自娱自乐。 这里还…...
【谷粒学院】MybatisPlus(1~17)
1.项目介绍 2.项目背景介绍 3.项目商业模式介绍 4.项目功能模块介绍 5.项目技术点介绍 6.项目技术点-MybatisPlus介绍 官网:http://mp.baomidou.com/ MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做…...
C++的输入输出
目录 一、基本的输入输出 二、I/O库头文件 三、标准输出流(cout) 四、标准输入流(cin) 五、标准错误流(cerr) 六、标准日志流(clog) 一、基本的输入输出 C 标准库提供了一组丰…...
RNN相关知识总结
目录RNN结构与原理1.模型总览2.反向传播LSTM结构与原理1.模型总览2.如何解决RNN梯度消失/爆炸问题?GRU结构及原理1.模型总览LSTM与GRU的区别RNN结构与原理 1.模型总览 上图是RNN的展开结构图,由输入层、隐藏层和输出层组成。当前时间步t 的隐藏状态hth_…...
wordpress插件的意义/大数据免费查询平台
from idaapi import * import timeloadaddr 0x10000 # 定义固件加载地址 eaStart 0x301111 loadaddr # 定义符号表的起始位置 eaEnd 0x321111 loadaddr # 定义符号表的结束地址while eaStart < eaEnd:# 循环修复函数名offset 0MakeStr(Dword(eaStart - offset), BA…...
网站开发企业/网店运营与推广
在使用maven插件执行spring-boot:run进行启动的时候,如果设置的断点进不去,要进行以下的设置。 1、添加jvm参数配置 在spring-boot的maven插件加上jvmArguments配置。 <project>...<build>...<plugins>...<plugin><groupI…...
现在在市场上做网站怎么样/百度关键词挖掘查排名工具
CSS美化INPUT placeholder效果。CSS代码美化文本框里的placeholder文字。 ::selection伪元素 简而言之:单冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。 伪元素由双冒号和伪元素名称组成。双冒号是在当前规范中引入的,用于区分伪类和伪元素…...
wordpress如何限制用户/seo优化培训班
openCV基本操作(一)图像读取视频读取截取部分图像数据颜色通道提取边界填充数值计算图像融合图像读取 视频读取 截取部分图像数据 颜色通道提取 边界填充 数值计算 图像融合 #!/usr/bin/env python # coding: utf-8# ## 图像基本操作# #### 环境配置地址…...
中小型网站建设怎么样/十大短视频平台排行榜
http://www.cocoachina.com/ios/20140409/8127.html...
南充做网站电话/有没有永久免费crm
函数:string.join() Python中有join()和os.path.join()两个函数,具体作用如下: join(): 连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串 os.path.join(): 将多个路径组合后…...