52.seata分布式事务

目录

1.事务的四大特性。 

2.分布式服务的事务问题。

3.seata。

3.1理论基础。

3.1.1CAP定理。

3.1.2BASE理论。

3.2初识Seata。

3.2.1Seata的架构。

3.2.2部署TC服务。

3.2.3微服务集成Seata。

3.3 seata提供的四种分布式事务解决方案。

3.3.1 XA模式。

3.3.1.1 XA模式原理。

3.3.1.2 实现XA模式。

3.3.2 AT模式。

3.3.2.1 AT模式原理。

3.3.2.2 AT模式的脏写问题 、AT模式的写隔离。

3.3.2.3 实现AT模式。

3.3.3 TCC模式。

3.3.3.1 TCC模式原理。

 3.3.3.2 TCC模式实现。

3.3.4 SAGA模式。

3.4 seata的四种模式对比。

3.5高可用。


1.事务的四大特性。 

ACID 是指数据库事务的四个特性,每个字母代表一个特性:

  1. 原子性(Atomicity):原子性要求数据库中的事务是一个不可分割的操作单元,要么全部成功执行,要么全部回滚,不会停留在中间状态。如果一个事务中的某个操作失败,那么整个事务都会被回滚到初始状态,不会对数据库产生任何影响。

  2. 一致性(Consistency):一致性要求数据库在进行事务处理后,必须保持数据的一致性状态。这意味着数据库在事务开始和结束时,必须遵循预定义的规则和约束条件,以确保数据的完整性和正确性。(要么全部执行,要么全部不执行,从而保持数据的一致性)(要么全部成功,要么全部失败

  3. 隔离性(Isolation):隔离性要求数据库中的每个事务都是相互隔离的,即每个事务对其他事务的操作应该是不可见的,以避免并发执行时的数据冲突和异常情况。数据库通过各种并发控制机制来实现隔离性,如锁机制、多版本并发控制(MVCC)等。(就是避免事务相互影响,比如两个事务以上对相同数据同时操作,或者其他事务还没成功提交就拿着用了)(事务在操作某些数据库记录时,把其他事务隔离在这些数据库记录外,事务之间不能相互影响。这样比较容易记

  4. 持久性(Durability):持久性要求一旦事务提交成功,数据库中的数据就应该永久保存,即使在系统故障或重启后也能够保持数据的一致性状态。数据库通过将数据写入稳定的存储介质(如磁盘)来实现持久性。

隔离性讲解:如果没有隔离性,可能会出现以下情况:

  1. 脏读:如果没有隔离性,一个事务可能会读取到另一个未提交事务的数据,导致读取到不正确的数据,从而产生误导性的结果。(我只是随便写写,你居然当真了?

  2. 不可重复读:缺乏隔离性可能导致同一个事务内多次读取同一数据时得到不一致的结果,影响事务的可靠性和一致性。(前脚读完,后脚就改了,数据还没给用户瞧,你就改了?

  3. 幻读:在缺乏隔离性的情况下,一个事务在读取某个范围内的记录时,另一个事务在该范围内插入了新的记录或删除旧的记录,导致第一个事务再次读取该范围时出现了之前不存在的记录,破坏了数据的一致性。(趁我不注意,你不是把我作业藏起来,就是改成你名字,你是要我怀疑人生?

  4. 数据完整性问题:缺乏隔离性可能导致事务之间相互干扰,从而破坏了数据库中数据的完整性和一致性,进而影响系统的可靠性和稳定性。(你改,我也改,大家一起改?

2.分布式服务的事务问题。

3.seata。

3.1理论基础。

3.1.1CAP定理。

C一致性:出现分区问题,访问独立分区节点的请求都被阻塞。

A可用性:出现分区问题,可以正常访问,该分区节点与其他分区节点的数据不一致性。

P分区容错性:在集群出现分区时,整个系统也要持续对外提供服务(如果不使用P,相当于访问所有分区节点都被阻塞)。

提醒:所以P在分布式系统中是必选的,A和C只能单选。

通过采用CP模型,Elasticsearch保证了数据的一致性和分区容错性。当有新的文档需要写入时,它会首先被写入主分片,然后主分片会将变更传播给对应的副本分片。只有当主分片和副本分片都确认写入成功后,写操作才会被认为是成功的,从而确保了数据的一致性。

P只要是分布式服务,使用了网络,就有可能网络故障出现分区的情况,P作用就是出现分区的情况下也能正常进行访问。

低可用性是因为当出现分区的时候,部分节点与其它节点失去连接,这时为了保证数据的一致性,访问该部分节点的请求都会被阻塞或拒绝。(如果是AP恰恰相反,可以访问,但是数据的一致性没了)

3.1.2BASE理论。

最终一致思想:不一致性、软状态(分别执行和提交)和最终一致的结合。

AP模式:不一致性(跟软状态很像) + 软状态 + 最终一致性。(允许暂时不一致性)

CP模式:强一致性 + 基本可用。(不允许暂时一致性)


3.2初识Seata。

Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布 式事务服务,为用户打造一站式的分布式解决方案。

官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。 

3.2.1Seata的架构。

3.2.2部署TC服务。

部署Seata的tc-server。

1.下载

首先我们要下载seata-server包,地址在http://seata.io/zh-cn/blog/download.html

2.解压

 在非中文目录解压缩这个zip包,其目录结构如下:

3.修改配置

修改conf目录下的registry.conf文件:

  内容如下:

registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype = "nacos" # 改了,用哪个注册中心就写那个的名字,然后只需要修改下面对应注册中心的参数就行nacos {application = "seata-tc-server" # 改了serverAddr = "127.0.0.1:80" # 改了group = "DEFAULT_GROUP" # 改了namespace = ""cluster = "SH" # 改了,SH代表上海,集群的名称username = "nacos" # 改了password = "nacos" # 改了}eureka {serviceUrl = "http://localhost:8761/eureka"application = "default"weight = "1"}redis {serverAddr = "localhost:6379"db = 0password = ""cluster = "default"timeout = 0}zk {cluster = "default"serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}consul {cluster = "default"serverAddr = "127.0.0.1:8500"aclToken = ""}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、etcd3type = "nacos"nacos {serverAddr = "127.0.0.1:80" # 改了namespace = ""group = "SEATA_GROUP" username = "nacos" # 改了password = "nacos" # 改了dataId = "seataServer.properties"}consul {serverAddr = "127.0.0.1:8500"aclToken = ""}apollo {appId = "seata-server"## apolloConfigService will cover apolloMetaapolloMeta = "http://192.168.1.204:8801"apolloConfigService = "http://192.168.1.204:8080"namespace = "application"apolloAccesskeySecret = ""cluster = "seata"}zk {serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""nodePath = "/seata/seata.properties"}etcd3 {serverAddr = "http://localhost:2379"}file {name = "file.conf"}
}

4.在nacos添加配置

特别注意,为了让tc服务的集群可以共享配置,我们选择了nacos作为统一配置中心。因此服务端配置文件seataServer.properties文件需要在nacos中配好。

格式如下:

配置内容如下:

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
​
# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

==其中的数据库地址、用户名、密码都需要修改成你自己的数据库信息。==

创建好如下:这里有两个配置文件,第一个是之前sentinel时使用的。第二个就是刚才创建的。

5.创建数据库表

特别注意:tc服务在管理分布式事务时,需要记录事务相关数据到数据库中,你需要提前创建好这些表。

新建一个名为seata的数据库,运行课前资料提供的sql文件:

这些表主要记录全局事务、分支事务、全局锁信息:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
​
-- ----------------------------
-- 分支事务表
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (`branch_id` bigint(20) NOT NULL,`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status` tinyint(4) NULL DEFAULT NULL,`client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime(6) NULL DEFAULT NULL,`gmt_modified` datetime(6) NULL DEFAULT NULL,PRIMARY KEY (`branch_id`) USING BTREE,INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
​
-- ----------------------------
-- 全局事务表
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`status` tinyint(4) NOT NULL,`application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`timeout` int(11) NULL DEFAULT NULL,`begin_time` bigint(20) NULL DEFAULT NULL,`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime NULL DEFAULT NULL,`gmt_modified` datetime NULL DEFAULT NULL,PRIMARY KEY (`xid`) USING BTREE,INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
​
SET FOREIGN_KEY_CHECKS = 1;

6.启动TC服务

启动TC服务:

进入bin目录,运行其中的seata-server.bat即可: 

 打开浏览器,访问nacos地址:http://localhost:8848,我配的端口是80,默认的是8848,然后进入服务列表页面,可以看到seata-tc-server的信息:

出现异常解决方法:

 解决方法是:打开seata-server.bat文件添加java的jdk8路径。

找到修改前的这一行:

修改后结果:

 然后重新启动就能成功了。

启动成功后,seata-server应该已经注册到nacos注册中心了。

3.2.3微服务集成Seata。

添加seata坐标集成微服务出现报错: 

添加坐标后,一启动服务就报错,启动失败。

解决方法是添加虚拟机选项:

--add-opens java.base/java.lang=ALL-UNNAMED

微服务集成seata分布式事务:

seata:registry:type: nacosnacos:server-addr: 127.0.0.1:80namespace: "" # 什么都不写就是publicgroup: DEFAULT_GROUPapplication: seata-tc-serverusername: nacospassword: nacostx-service-group: seata-demo # 事务组名称service:vgroup-mapping: # 事务组与cluster集群的映射关系seata-demo: SH

3.3 seata提供的四种分布式事务解决方案。

Seata提供了四种不同的分布式事务解决方案:

XA 模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC 模式:最终一致的分阶段事务模式,有业务侵入
AT 模式:最终一致的分阶段事务模式,无业务侵入,也是 Seata 的默认模式
SAGA 模式:长事务模式,有业务侵入

3.3.1 XA模式。

seata的RM也仅仅是在数据库的接口基础上做了一层简单的封装,实际上seata的RM它也是调用数据库的RM实现。(主流数据库基本都实现了RM)。这种方法依赖数据库底层的实现,如果数据库没有实现XA模式,那么就用不了。

3.3.1.1 XA模式原理。

XA模式原理

XA 规范 是 X/Open 组织定义的分布式事务处理(DTPDistributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。

seataXA模式:

seataXA模式做了一些调整,但大体相似:

RM一阶段的工作:

注册分支事务到 TC
执行分支业务 sql 但不提交
报告执行状态到 TC

TC二阶段的工作:

TC 检测各分支事务执行状态
a. 如果都成功,通知所有 RM 提交事务
b. 如果有失败,通知所有 RM 回滚事务

RM二阶段的工作:

接收 TC 指令,提交或回滚事务

总结:

XA模式的优点是什么?

事务的强一致性,满足 ACID 原则。
常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
依赖关系型数据库实现事务
3.3.1.2 实现XA模式。

实现XA模式

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

1. 修改application.yml文件(每个参与事务的微服务),开启XA模式:

seata:  data-source-proxy-mode: XA # 开启数据源代理的XA模式

2. 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法:

@Override
@GlobalTransactional
public Long create(Order order) {// 创建订单orderMapper.insert(order);// 扣余额 ...略// 扣减库存 ...略return order.getId();
}

3. 重启服务并测试

访问的时候出现该异常解决方法:

 解决方法:

把数据库驱动类从8.0.27换成8.0.11就能成功启动。指定的url还要加上时区。

url: jdbc:mysql://localhost:3306/seata_demo?serverTimezone=UTC
    <properties><mysql.version>8.0.11</mysql.version></properties><dependencyManagement><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency></dependencies></dependencyManagement>

3.3.2 AT模式。

3.3.2.1 AT模式原理。

AT模式原理:

总结:

简述AT模式与XA模式最大的区别是什么?

•  XA 模式一阶段不提交事务,锁定资源; AT 模式一阶段直接提交,不锁定资源。
•  XA 模式依赖数据库机制实现回滚; AT 模式利用数据快照实现数据回滚。
•  XA 模式强一致; AT 模式最终一致
3.3.2.2 AT模式的脏写问题 、AT模式的写隔离。

AT模式的脏写问题

AT模式的写隔离(seata管理的全局事务):

全局锁只对seata管理的全局事务有用,非seata管理的事务不需要全局锁就能直接操作。

AT模式的写隔离(非seata管理的全局事务):

全局锁只对seata管理的全局事务有用,非seata管理的事务不需要全局锁就能直接操作。

总结:

AT模式的优点:

•  一阶段完成直接提交事务,释放数据库资源,性能比较好
•  利用全局锁实现读写隔离
•  没有代码侵入,框架自动完成回滚和提交

AT模式的缺点:

•  两阶段之间属于软状态,属于最终一致
•  框架的快照功能会影响性能,但比 XA 模式要好很多
3.3.2.3 实现AT模式。

实现AT模式:

要注意这里的两个表可能不在同一个库中。

下面是seata-at.sql文件里面的两个表,注意两个表可能不在同一个库中。

-- ----------------------------
-- 这一张表是放在TC服务使用的库中,在之前我们在nacos配置的TC数据库配置指定了使用seata库,所以放在seata库
-- ----------------------------
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;-- ----------------------------
-- 这一张表是放在集成了seata的微服务的配置文件中指定了的数据库的库,集成的seata的微服务我都是使用seata-demo库,所以就放在这个库上。
-- ----------------------------
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;

3.3.3 TCC模式。

3.3.3.1 TCC模式原理。

TCC模式原理:

总结:

TCC模式的每个阶段是做什么的?

•  Try :资源检查和预留
•  Confirm :业务执行和提交
•  Cancel :预留资源的释放

TCC的优点是什么?

•  一阶段完成直接提交事务,释放数据库资源,性能好
•  相比 AT 模型,无需生成快照,无需使用全局锁,性能最强
•  不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点是什么?

•  有代码侵入,需要人为编写 try Confirm Cancel 接口,太麻烦
•  软状态,事务是最终一致
•  需要考虑 Confirm 和Cancel的失败情况,做好幂等处理(

幂等性是指无论调用多少次,对系统的状态都只有一次改变。在 TCC 模式中,如果参与者因为各种原因执行了多次,那么整个事务的状态就会变得不确定,因此需要确保每个参与者操作具有幂等性。

例如,在一个转账事务中,如果在“try”阶段时发生了错误,导致“try”被多次调用,那么在“confirm”或“cancel”阶段也可能会被多次调用。如果这些操作不具有幂等性,就会出现重复的转账操作,导致数据不一致。

 3.3.3.2 TCC模式实现。

TCC的空回滚和业务悬挂:

空回滚:就是调用某分支事务的时候因为阻塞导致失败,然后TC通知所有分支事务做回滚,但是失败分支事务都没有执行try(没有预留资源),这时就需要做空回滚。(如果让它执行cancel方法会执行失败,然后继续尝试执行cancel)。

业务悬挂:已经空回滚的业务,继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。,因为该全局事务已经结束了我们这时要阻止它执行。

TCC实现案例:

(@BusinessActionContextParameter(paramName = "param") 注解标记的参数会放到BusinessActionContext上下文对象中,到时候通过该上下文对象可以获取该参数。

根据上面的理解分析来动手实现TCC模式:

1.导入account_freeze_tbl数据库表,放在微服务使用的库中。

DROP TABLE IF EXISTS `account_freeze_tbl`;
CREATE TABLE `account_freeze_tbl`  (`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`freeze_money` int(11) UNSIGNED NULL DEFAULT 0,`state` int(1) NULL DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',PRIMARY KEY (`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;

2.定义account_freeze_tbl表的实体类。

@Data
@TableName("account_freeze_tbl")
public class AccountFreeze {@TableId(type = IdType.INPUT)private String xid;private String userId;private Integer freezeMoney;private Integer state;public static abstract class State {public final static int TRY = 0;public final static int CONFIRM = 1;public final static int CANCEL = 2;}
}

3.定义操作account_freeze_tbl表的mybatis-plus的接口类。

@Mapper
public interface AccountFreezeMapper extends BaseMapper<AccountFreeze> {
}

4.定义TCC两个阶段的三个接口方法。

@LocalTCC
public interface AccountTCCService {@TwoPhaseBusinessAction(name = "deduct",commitMethod = "fonfirm",rollbackMethod = "cancel")void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,@BusinessActionContextParameter(paramName = "money") int money);boolean confirm(BusinessActionContext context);boolean cancel(BusinessActionContext context);
}

我们要做的业务操作接口:

@Mapper
public interface AccountMapper extends BaseMapper<Account> {@Update("update account_tbl set money = money - ${money} where user_id = #{userId}")int deduct(@Param("userId") String userId, @Param("money") int money);@Update("update account_tbl set money = money + ${money} where user_id = #{userId}")int refund(@Param("userId") String userId, @Param("money") int money);
}

5.实现TCC两个阶段的三个方法。

由于 confirmcancel 方法的执行不会对数据进行修改,因此不需要使用 @Transactional 注解。这些方法通常只需要保证幂等性即可,确保多次调用不会产生副作用。

@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {@Autowiredprivate AccountMapper accountMapper;@Autowiredprivate AccountFreezeMapper freezeMapper;@Override@Transactionalpublic void deduct(String userId, int money) {//0.获取全局事务idString xid = RootContext.getXID();//1.判断freeze中是否有冻结记录,如果有,一定是cancel执行过,我要拒绝业务。AccountFreeze oldFreeze = freezeMapper.selectById(xid);if (oldFreeze != null){//cancel执行过,我要拒绝业务return;}//1.扣减可用余额(数据库中的字段使用unsigned关键词修饰,如果为负会报错,然后回滚,所以可以省略余额判断)accountMapper.deduct(userId,money);//2.冻结金额,事务状态AccountFreeze freeze = new AccountFreeze();freeze.setUserId(userId);freeze.setFreezeMoney(money);freeze.setState(AccountFreeze.State.TRY);freeze.setXid(xid);freezeMapper.insert(freeze);}@Overridepublic boolean confirm(BusinessActionContext context) {//1.获取事务idString xid = context.getXid();//2.根据id删除冻结及记录int count = freezeMapper.deleteById(xid);return count == 0;}@Overridepublic boolean cancel(BusinessActionContext context) {//注意:这里要保留记录,做空回滚和业务悬挂判断时需要用。String xid = context.getXid();String userId = context.getActionContext("userId").toString();//0.查询冻结记录AccountFreeze freeze = freezeMapper.selectById(xid);//1.空回滚的判断,判断freeze是否为null,为null证明try没执行,需要空回滚if (freeze == null){//证明try没执行,需要空回滚freeze = new AccountFreeze();freeze.setUserId(userId);freeze.setFreezeMoney(0);freeze.setState(AccountFreeze.State.CANCEL);freeze.setXid(xid);freezeMapper.insert(freeze);return true;}//2.幂等判断if (freeze.getState() == AccountFreeze.State.CANCEL){//已经处理过一次CANCEL了,无需重复处理return true;}//1.恢复可用余额accountMapper.refund(freeze.getUserId(),freeze.getFreezeMoney());//2.将冻结金额清零,状态改为CANCELfreeze.setFreezeMoney(0);freeze.setState(AccountFreeze.State.CANCEL);int count = freezeMapper.updateById(freeze);return count == 1;}
}

6.修改控制类。

@RestController
@RequestMapping("account")
public class AccountController {@Autowiredprivate AccountTCCService accountTCCService;//原本是直接使用的AccountService接口的方法@PutMapping("/{userId}/{money}")public ResponseEntity<Void> deduct(@PathVariable("userId") String userId, @PathVariable("money") Integer money){accountTCCService.deduct(userId, money);return ResponseEntity.noContent().build();}
}

业务执行失败,回滚后的记录。

3.3.4 SAGA模式。

3.4 seata的四种模式对比。

3.5高可用。

TC服务的高可用和异地容灾(异地容灾就是把微服务部署到不同机房。)

1.模拟异地容灾的TC集群

计划启动两台seata的tc服务节点:

节点名称ip地址端口号集群名称
seata127.0.0.18091SH
seata2127.0.0.18092HZ

之前我们已经启动了一台seata服务,端口是8091,集群名为SH。

现在,将seata目录复制一份,起名为seata2

修改seata2/conf/registry.conf内容如下:

registry {# tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等type = "nacos"
​nacos {# seata tc 服务注册到 nacos的服务名称,可以自定义application = "seata-tc-server"serverAddr = "127.0.0.1:8848"group = "DEFAULT_GROUP"namespace = ""cluster = "HZ"username = "nacos"password = "nacos"}
}
​
config {# 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置type = "nacos"# 配置nacos地址等信息nacos {serverAddr = "127.0.0.1:8848"namespace = ""group = "SEATA_GROUP"username = "nacos"password = "nacos"dataId = "seataServer.properties"}
}

进入seata2/bin目录,然后运行命令:

seata-server.bat -p 8092

打开nacos控制台,查看服务列表:

点进详情查看:

2.将事务组映射配置到nacos

接下来,我们需要将tx-service-group与cluster的映射关系都配置到nacos配置中心。

新建一个配置:

配置的内容如下:

# 事务组映射关系
service.vgroupMapping.seata-demo=SH
​
service.enableDegrade=false
service.disableGlobalTransaction=false
# 与TC服务的通信配置
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
# RM配置
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
# TM配置
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
​
# undo日志配置
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
client.log.exceptionRate=100

3.微服务读取nacos配置

接下来,需要修改每一个微服务的application.yml文件,让微服务读取nacos中的client.properties文件:

seata:config:type: nacos # 必须要学这个,不然会报错nacos:server-addr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUPdata-id: client.properties

重启微服务,现在微服务到底是连接tc的SH集群,还是tc的HZ集群,都统一由nacos的client.properties来决定了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/205767.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全国第一届学生(青年)运动会女子拳击比赛60公斤冠军载誉归来

11月16日&#xff0c;参加全国第一届学生&#xff08;青年&#xff09;运动会女子拳击比赛60公斤冠军阿依古再丽麦合苏提抵达和田。 中华人民共和国第一届学生&#xff08;青年&#xff09;运动会拳击比赛11月12日在广西贺州市钟山县体育馆落下帷幕&#xff0c;本届比赛新疆拳击…

原始类型 vs. 对象(基本类型 vs. 引用类型)

原始类型 首先我们先看一段代码&#xff1a; let age 30; let oldAge age; age 31; console.log(age); console.log(oldAge);在 JavaScript 中&#xff0c;原始类型的赋值是通过值复制的方式进行的&#xff0c;而不会相互影响。只有对象类型的值才是通过引用复制的方式进行…

服务案例|故障频发的一周,居然睡得更香!

医院运维有多忙&#xff1f; 医院运维&#xff0c;听起来平平无奇毫不惊艳&#xff0c;但其中的含金量&#xff0c;可不是“维持系统正常运行”就能总结的。毕竟医院对业务连续性的超高要求&#xff0c;让运维面对的问题都是暂时的&#xff0c;下一秒可能就有新问题需要发现解…

Vue3 快速上手-基于Vue2基础

Vue3 快速上手-基于Vue2基础 前言Vue3快速上手1.Vue3简介2.Vue3带来了什么1.性能的提升2.源码的升级3.拥抱TypeScript4.新的特性 一、创建Vue3.0工程1.使用 vue-cli 创建2.使用 vite 创建 二、常用 Composition API1.拉开序幕的setup2.ref函数3.reactive函数4.Vue3.0中的响应式…

禁止安装新软件怎么设置(超详细图文介绍)

很多公司的网管向我们反应&#xff0c;总是有员工随意下载软件&#xff0c;并且不去正规网站、正规官网下载&#xff0c;导致公司的电脑总是又卡又慢&#xff0c;网管的工作很难开展。 此时就需要对公司安装软件的情况&#xff0c;进行统一管控了。 方法一&#xff1a;适合个人…

这是基础校园二手交易框架

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>校园二手交易</title> <style> /* Reset stylesheet */ * { margin: 0; padding: 0; box-s…

七牛云产品使用介绍之CDN篇

上一篇介绍了七牛云的Kodo对象存储&#xff0c;并用Java SDK实现将本地文件上传到bucekt&#xff0c;接下来是对CDN产品的介绍 CDN&#xff08;内容分发网络&#xff09;&#xff1a;通过多级缓存实现对Kodo中的资源或者自己网站的资源的加速访问&#xff0c;让你的系统更快更强…

复旦、人大等发布大五人格+MBTI测试 角色扮演AI特质还原率达82.8%

近期&#xff0c;由复旦大学和中国人民大学合作的Chat凉宫春日团队发布了一项关于AI角色扮演的研究。该研究强调了良好的人设还原度对于评价AI角色扮演的重要性&#xff0c;特质还原率高达82.8%。研究使用了大五人格的NEO-FFI问卷和MBTI的16Personalities测试&#xff0c;并通过…

交流充电桩测试参考标准是哪些

功能性能测试&#xff1a;这是评价交流充电桩性能的重要标准&#xff0c;包括充电效率、充电稳定性、充电模式等。充电效率主要检查充电桩的充电功率、充电时间等&#xff1b;充电稳定性主要检查充电桩的电压、电流波动等&#xff1b;充电模式主要检查充电桩的恒流充电、恒压充…

数据集笔记:Pems 自行下载数据+python处理

以下载District 4的各station每5分钟的车速为例 1 PEMS网站下载数据 点击红色的 选择需要的station和区域&#xff0c;点击search&#xff0c;就是对应的数据&#xff0c;点击数据即可下载 &#xff08;这个是station每5分钟的速度数据&#xff09; 2 pems 速度数据 2.1 每一…

Linux系统管理与服务器安全:构建稳健云数据中心

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在当今数字化时代&#xff0c;云数据中心已经成…

今日定音,博通以610亿美元成功收购VMware | 百能云芯

博通&#xff08;Broadcom&#xff09;日前宣布&#xff0c;已获得中国监管机构的批准&#xff0c;将于今日完成对云计算公司VMware的收购交易。这意味着&#xff0c;610亿美元的收购案正式收关。 据悉&#xff0c;中国市场监管总局在11月21日晚发布了有关附加限制性条件批准博…