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.mzph.cn/news/159018.shtml

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

相关文章

005 OpenCV直方图

目录 一、环境 二、直方图原理概述 三、代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、直方图原理概述 OpenCV是一个广泛使用的开源计算机视觉库&#xff0c;它提供了许多用于图像处理和分析的函数和算法。其中&#xff…

分享-Spss下载含spss25.spss26.spss27等版本

为了学习spss买的&#xff0c;分享安装程序给大家 SPSS 27是一款用于统计分析和数据挖掘的软件&#xff0c;以下是SPSS 27的功能介绍和配置建议&#xff1a; 功能介绍&#xff1a; 数据管理&#xff1a;SPSS 27可以对数据进行管理和清洗&#xff0c;包括数据输入、缺失值处理…

微软和 OpenAI 的桥段,30 年前的数据库圈也上演过

历史不会重复, 却压着相同的韵脚。 这是一段发生在 30 年前的历史&#xff0c;也是在当时最热的技术领域-数据库&#xff0c;主角之一同样是微软。 论白嫖收购&#xff0c;微软一直是行业内的佼佼者&#xff08;虽然也发生过收购诺基亚这样的翻车事故&#xff09;。这也是它基…

自动化测试 —— 元素定位

1.什么是自动化测试 自动化测试的概念:软件自动化测试就是通过测试工具或者其他手段&#xff0c;按照测试人员的预定计划对软件产品进行自动化测试&#xff0c;他是软件测试的一个重要组成部分&#xff0c;能够完成许多手工测试无法完成或者难以实现的测试工作&#xff0c;正确…

9.2 Windows驱动开发:内核解析PE结构导出表

在笔者的上一篇文章《内核特征码扫描PE代码段》中LyShark带大家通过封装好的LySharkToolsUtilKernelBase函数实现了动态获取内核模块基址&#xff0c;并通过ntimage.h头文件中提供的系列函数解析了指定内核模块的PE节表参数&#xff0c;本章将继续延申这个话题&#xff0c;实现…

如何用CHAT写励志文章?

问CHAT&#xff1a;写一篇以《过了60岁要积极面对身体疾病的坎儿》为题目&#xff0c;写一篇300字励志文章 CHAT回复&#xff1a; 标题&#xff1a;《过了60岁要积极面对身体疾病的坎儿》 人生&#xff0c;有时会像一趟不期而遇的旅程&#xff0c;各自带着乐观或悲观、阳光或…

(论文阅读51-57)图像描述3 53

51.文献阅读笔记&#xff08;KNN&#xff09; 简介 题目 Exploring Nearest Neighbor Approaches for Image Captioning 作者 Jacob Devlin, Saurabh Gupta, Ross Girshick, Margaret Mitchell, C. Lawrence Zitnick, arXiv:1505.04467 原文链接 http://arxiv.org/pdf/1…

如何使用无代码系统搭建软件平台?有哪些开源无代码开发平台?

无代码是什么 无代码开发&#xff0c;也称为零代码&#xff08;Zero Code&#xff09;开发&#xff0c;是一种技术概念。无代码开发无需代码基础&#xff0c;适合业务人员、IT开发及其他各类人员使用。他们通过无代码开发平台快速构建应用&#xff0c;并适应各种需求变化&#…

深入理解Java虚拟机-GC

深入理解Java虚拟机-GC 当需要排查各种内存溢出、内存泄漏时&#xff0c;当垃圾回收成为系统到达更高并发量的瓶颈时&#xff0c;我们必须对内存动态分配和内存回收技术这样的“自动化”技术采用必要的监控和调节。 Java堆和方法区&#xff1a;一个接口的多个实现类需要的内存…

element表格头部加入图标

首先看看效果 下面是代码 <el-table-column prop"integralBalance"><template slot"header" slot-scope"scope"><div style"display: flex;justify-content: center;align-items: center;">积分余额<i class&qu…

创建 Springboot 项目

前言 创建 Spring Boot 项目是很多Java开发人员入门的重要一步&#xff01; 欢迎来到本篇关于创建 Spring Boot 项目的博客&#xff01;Spring Boot作为一个快速、便捷的开发框架&#xff0c;为我们提供了简化和加速应用程序开发的利器。 在这个数字化时代&#xff0c;快速响…

C语言从入门到实战——数组和指针的强化练习题

数组和指针的强化练习题 前言1. sizeof和strlen的对比1.1 sizeof1.2 strlen1.3 sizeof和strlen的对⽐ 2. 数组和指针笔试题解析2.1 一维数组2.2 字符数组2.3 二维数组 3. 指针运算笔试题解析3.1 题目1&#xff1a;3.2 题目23.3 题目33.4 题目43.5 题目53.6 题目63.7 题目7 前言…

rabbit MQ的延迟队列处理模型示例(基于SpringBoot死信模式)

说明&#xff1a; 生产者P 往交换机X&#xff08;typedirect&#xff09;会发送两种消息&#xff1a;一、routingKeyXA的消息&#xff08;消息存活周期10s&#xff09;&#xff0c;被队列QA队列绑定入列&#xff1b;一、routingKeyXB的消息&#xff08;消息存活周期40s&#xf…

迪文科技工业串口屏(DMG10600C070-03WTC)更新程序烧录刷机

迪文科技工业串口屏(DMG10600C070-03WTC)更新程序烧录刷机 问题 使用SD卡上电烧录&#xff0c;SD卡文件路径如下&#xff1a; 烧录时&#xff0c;无法写入&#xff0c;成功烧录文件数为0 解决方法 格式化读卡器 格式化脚本 echo off %1 %2 ver|find "5.">…

【Redis篇】简述Redis | 详解Redis命令

文章目录 &#x1f38d;什么是Redis&#x1f38d;Redis特点&#x1f38d;Redis应用场景&#x1f354;Windows安装Redis⭐启动Redis &#x1f33a;Redis数据类型&#x1f33a;Redis常用命令⭐字符串string操作命令⭐哈希hash操作命令⭐列表list操作命令⭐集合set操作命令⭐有序集…

C++实战学习笔记

文章目录 erase()uniquevector的insert()std::string::npos erase() &#xff08;1&#xff09;erase(pos,n); 删除从pos开始的n个字符&#xff0c;比如erase(0,1)就是删除第一个字符 &#xff08;2&#xff09;erase(position);删除position处的一个字符(position是个string类…

linux下流媒体压力测试工具的使用

前言 因为领导要求做linux的推拉流时服务器压力测试&#xff0c;于是在网上找了找。一顿操作下来&#xff0c;发现很多软件盗用一款名为srs-bench的开源软件。 该代码仓库有详细的使用说明&#xff0c;而且可以在issues中找到可能会遇到的问题的解决办法 需要下载该仓库的源…

RK3568开发板在工控工业物联网网关方面的应用

在数字化转型的浪潮中&#xff0c;工控物联网关产品扮演着重要的角色。这些产品通过连接工业设备和网络&#xff0c;为数据传输和分析提供了便利。而迅为RK3568核心板作为一款高性能的芯片&#xff0c;为工控物联网关产品的性能提升和功能扩展提供了强大的支持。 迅为RK3568核心…

基于JAVA+SpringBoot+VUE+微信小程序的前后端分离咖啡小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着社会的快速发展和…

2023年约特干故城夜间演艺《万方乐奏有于阗》完美谢幕

11月19日&#xff0c;记者走进约特干故城看到演员在欢乐地跳着刀郎舞和古典舞&#xff0c;庆祝今年以来夜间演艺《万方乐奏有于阗》演出200场完美谢幕。 11月19日在约特干故城&#xff0c;演员正在表演迎宾乐舞。阿卜力克木依卜拉依木摄 当天晚上&#xff0c;城楼上旌旗猎猎&am…