spring cloud之分布式事务

写在前面

1:分布式事务介绍

参考MySQL之分布式事务 。

2:seata实战

架构图:
在这里插入图片描述
可以看到seata在这里作为协调者的角色,协调所有事务的提交以及回滚,其中seata使用MySQL存储每个分支事务的执行状态信息,以便在需要回滚等操作时可以获取到相应的信息进行回滚操作。这里seata服务也是作为一个微服务节点来运行,因此也需要将信息注册到nacos中,这样可以方便的来做服务发现。

2.1:搭建seata服务

首先在这里 现在seata的运行jar包,在运行jar包之前还需要做一些配置操作。

  • 首先持久化模式
    这里我们使用的持久化模式是MySQL,修改如下:
## transaction log store, only used in server side
store {## store mode: file、db## 【改动点01】 - 替换成db类型mode = "db"
  • 接着对应的修改数据库连接信息
store {mode = "db"## 【改动点02】 - 更改参数## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource = "druid"## mysql/oracle/postgresql/h2/oceanbase etc.dbType = "mysql"driverClassName = "com.mysql.jdbc.Driver"## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection paramurl = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"user = "root"password = ""minConn = 5maxConn = 30globalTable = "global_table"branchTable = "branch_table"lockTable = "lock_table"queryLimit = 100}
}
  • 创建seata数据库和表
    注意创建在seata数据库中。
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(`xid`                       VARCHAR(128) NOT NULL,`transaction_id`            BIGINT,`status`                    TINYINT      NOT NULL,`application_id`            VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name`          VARCHAR(128),`timeout`                   INT,`begin_time`                BIGINT,`application_data`          VARCHAR(2000),`gmt_create`                DATETIME,`gmt_modified`              DATETIME,PRIMARY KEY (`xid`),KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(`branch_id`         BIGINT       NOT NULL,`xid`               VARCHAR(128) NOT NULL,`transaction_id`    BIGINT,`resource_group_id` VARCHAR(32),`resource_id`       VARCHAR(256),`branch_type`       VARCHAR(8),`status`            TINYINT,`client_id`         VARCHAR(64),`application_data`  VARCHAR(2000),`gmt_create`        DATETIME,`gmt_modified`      DATETIME,PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(`row_key`        VARCHAR(128) NOT NULL,`xid`            VARCHAR(96),`transaction_id` BIGINT,`branch_id`      BIGINT       NOT NULL,`resource_id`    VARCHAR(256),`table_name`     VARCHAR(32),`pk`             VARCHAR(36),`gmt_create`     DATETIME,`gmt_modified`   DATETIME,PRIMARY KEY (`row_key`),KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;
  • 创建undo_log
    注意该表创建在应用对应的数据库中,咱们这里就是geekbang_coupon_db,用来存储分支事务已经提交的事务的回滚操作信息:
CREATE TABLE IF NOT EXISTS `undo_log`
(`id`            BIGINT(20)   NOT NULL AUTO_INCREMENT COMMENT 'increment id',`branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(100) NOT NULL COMMENT 'global transaction id',`context`       VARCHAR(128) 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     NOT NULL COMMENT 'create datetime',`log_modified`  DATETIME     NOT NULL COMMENT 'modify datetime',PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
  • 开启服务发现
    配置注册自身信息到nacos中:
registry {# 【改动点01】 - type变成nacostype = "nacos"# 【改动点02】 - 更换nacos {application = "seata-server-dyq"serverAddr = "192.168.10.62:8858"group = "myGroup"namespace = "dev"cluster = "default"username = ""password = ""}}

注意这里的group和namespace和咱们的应用保持一致,最后我们就可以通过bin目录下的启动脚本来启动应用了:

D:\programs\seata\seata\seata-server-1.4.2\bin>seata-server.bat
[0.003s][warning][gc] -Xloggc is deprecated. Will use -Xlog:gc:D:\programs\seata\seata\seata-server-1.4.2\bin\\../logs/seata_gc.log instead.
[0.069s][info   ][gc] Using G1

如果一切正常的话就可以在nacos中看到服务信息了:
在这里插入图片描述

2.2:seata AT模式

AT模式的角色如下:

TC:transaction coordinator,即seata server,扮演协调者的角色,负责协调全局事务的提交和回滚,维护全局和分支事务的状态。
TM:transaction manager,发一起一个全部事务,并对全局事务的提交和回滚进行决议,在AT方案中,TM由发起事务的微服务扮演。
RM:resource manager,资源管理器,向TC上报分支事务的状态,负责分支事务的提交和回滚,由参与的微服务扮演。

以custom模块调用template模块为例看下具体处理过程,可参考下图:
在这里插入图片描述
主要分为两个阶段:

1:各个微服务模块注册分支事务信息到seata,执行CRUD操作,seata会根据具体的操作生成对应的分支事务的回滚操作,并将信息存储到表undo_log中
2:如果是TM最终决议commit,则seata通知所有的rm提交事务,并删除undo_log表中的回滚数据,如果是TM最终决议rollabck,则所有的RM执行undo_log的回滚操作,回滚修改,最终也会删除undo_log的回滚数据。

接着我们来改造微服务,首先在custom和template模块中引入依赖:

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

和添加配置:

spring:...cloud:alibaba:seata:tx-service-group: seata-server-group
seata:application-id: coupon-template-serv-streamregistry:type: nacosnacos:application: seata-server-dyqserver-addr: 192.168.10.62:8868namespace: devgroup: myGroupcluster: defaultservice:vgroup-mapping:seata-server-group: default

接着在custom添加测试的接口,具体代码参看源码,用来调用template服务删除服务,做如下的事情:

1:custom调用template将coupon_template表对应templateId的信息标记为删除
2:cusotm服务本身标记coupon表tempalteId对应的信息标记为无效

首先在接口添加注解@GlobalTransactional(name = "coupon-customer-serv", rollbackFor = Exception.class),如下:

@DeleteMapping("templateCouponTemplate")
@GlobalTransactional(name = "coupon-customer-serv", rollbackFor = Exception.class)
public void templateCouponTemplate(@RequestParam("templateId") Long templateId) {customerService.deleteCouponTemplate(templateId);
}

来手动抛个异常:

@Override
@Transactional
public void deleteCouponTemplate(Long templateId) {templateService.deleteTemplate(templateId);couponDao.deleteCouponInBatch(templateId, CouponStatus.INACTIVE);// 模拟分布式异常throw new RuntimeException("AT分布式事务挂球了");
}

为了查看相关表生成的记录,我们可以在异常这行打个debug,然后看下数据,此时相当于custom调用template返回,custom的本地事务还没有提交时:
在这里插入图片描述
在这里插入图片描述
其中undo_log存储了回滚需要的信息,以json格式存储:

{"@class": "io.seata.rm.datasource.undo.BranchUndoLog","xid": "10.77.0.33:8091:7638587481204744198","branchId": 7638587481204744199,"sqlUndoLogs": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.undo.SQLUndoLog","sqlType": "UPDATE","tableName": "coupon_template","beforeImage": {"@class": "io.seata.rm.datasource.sql.struct.TableRecords","tableName": "coupon_template","rows": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 2},{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "available","keyType": "NULL","type": -7,"value": true}]]}]]},"afterImage": {"@class": "io.seata.rm.datasource.sql.struct.TableRecords","tableName": "coupon_template","rows": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 2},{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "available","keyType": "NULL","type": -7,"value": false}]]}]]}}]]
}

2.3:seata TCC模式

包含try,confirm,cancel,三个阶段:
在这里插入图片描述
其中各个阶段所作的事情如下:

try:锁定资源,相当于prepare
confirm:执行具体的操作,相当于commit
cancel:解锁try锁定的资源,相当于rollback

为了实现TCC,我们首先需要注册TCC接口:

@LocalTCC
public interface CouponTemplateServiceTCC extends CouponTemplateService {@TwoPhaseBusinessAction(name = "deleteTemplateTCC",commitMethod = "deleteTemplateCommit",rollbackMethod = "deleteTemplateCancel")void deleteTemplateTCC(@BusinessActionContextParameter(paramName = "id") Long id);void deleteTemplateCommit(BusinessActionContext context);void deleteTemplateCancel(BusinessActionContext context);
}

为了支持try操作,在表中增加lock字段,如下:

alter table coupon_templateadd locked tinyint(1) default 0 null;

对应的实体也需要修改:

@Column(name = "locked", nullable = false)
private Boolean locked;

接着tcc三个阶段对应的方法如下:

// TCC 的T 通过修改lock字段值,完成锁定
@Override
@Transactional
public void deleteTemplateTCC(Long id) {CouponTemplate filter = CouponTemplate.builder().available(true).locked(false).id(id).build();CouponTemplate template = templateDao.findAll(Example.of(filter)).stream().findFirst().orElseThrow(() -> new RuntimeException("Template Not Found"));template.setLocked(true);templateDao.save(template);
}// TCC 的第一个C
@Override
@Transactional
public void deleteTemplateCommit(BusinessActionContext context) {Long id = Long.parseLong(context.getActionContext("id").toString());CouponTemplate template = templateDao.findById(id).get();template.setLocked(false);template.setAvailable(false);templateDao.save(template);log.info("TCC committed");
}// TCC 的第二个C
@Override
@Transactional
public void deleteTemplateCancel(BusinessActionContext context) {Long id = Long.parseLong(context.getActionContext("id").toString());Optional<CouponTemplate> templateOption = templateDao.findById(id);// 空回滚if (templateOption.isPresent()) {CouponTemplate template = templateOption.get();// 通过修改lock值解锁template.setLocked(false);templateDao.save(template);}log.info("TCC cancel");
}

done!!!

写在后面

参考文章列表

MySQL之分布式事务 。

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

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

相关文章

gitlab设置/修改克隆clone地址端口

最近由于公司要停测试库云服务器? 什么?要停测试库服务器??? 是的! 你没听错。 真是醉了,多大的集团,为了省钱,也真是拼了, 作为开发人员,没有测试服务器,犹如断臂之人。 所以,在之前搭建环境的时候都没有写文档,今天算是弥补上,以后都可以作为参考了, …

MySQL和Redis的事务有什么异同?

MySQL和Redis是两种不同类型的数据库管理系统&#xff0c;它们在事务处理方面有一些重要的异同点。 MySQL事务&#xff1a; ACID属性&#xff1a; MySQL是一个关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;支持ACID属性&#xff0c;即原子性&#xff08;Ato…

23种设计模式,你学废了嘛?【建议收藏起来】

23种经典设计模式共分为3种类型&#xff0c;分别是创建型、结构型和行为型。 今天&#xff0c;我们把这3种类型分成3个对应的小模块&#xff0c;逐一带你回顾一下每一种设计模式的原理、实现、设计意图和应用场景。 还是那句话&#xff0c;如果你看了之后&#xff0c;感觉都有…

Kafka3学习笔记

文章目录 一、Kafka概述和入门1、Kafka概述1.1 定义1.2 消息队列1.3 Kafka 基础架构 2、Kafka 快速入门2.1 安装部署2.2 集群启停脚本 3、Kafka 命令行操作3.1 Topic命令行操作3.2 生产者命令行操作3.3 消费者命令行操作 二、Kafka核心概念详解1、Kafka 生产者1.1 生产者消息发…

MySQL 多表查询

文章目录 多表关系一对多多对多一对一 查询合并查询&#xff08;笛卡尔积&#xff0c;会展示所有组合结果&#xff09;&#xff1a;内连接查询外连接查询自连接查询联合查询 union, union all子查询单行子查询列子查询行子查询表子查询 :::success 多表查询&#xff0c;也称为…

类和对象 第三部分第二小节:空指针访问成员函数

C中空指针也可以调用成员函数的&#xff0c;但是也要注意有没有用到this指针&#xff0c;如果有用到this指针&#xff0c;需要加以保证代码的健壮性 代码案例 出现报错 报错原因&#xff1a;因为新建的指针是空&#xff0c;所以this指代的对象为空&#xff0c;因此没有成员变量…

Flume1.9基础学习

文章目录 一、Flume 入门概述1、概述2、Flume 基础架构2.1 Agent2.2 Source2.3 Sink2.4 Channel2.5 Event 3、Flume 安装部署3.1 安装地址3.2 安装部署 二、Flume 入门案例1、监控端口数据官方案例1.1 概述1.2 实现步骤 2、实时监控单个追加文件2.1 概述2.2 实现步骤 3、实时监…

无人机路径优化(八):五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划(提供MATLAB代码)

一、五种算法&#xff08;DBO、LO、SWO、COA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖行为的启发…

华为路由器IPv6基础配置

1. R2的两个接口均采用静态IPv6地址配置方法 2. R1的GigabitEthernet0/0/3接口采用无状态 地址配置 3. R3的GigabitEthernet0/0/3接口采用DHCPv6 的方式配置IPv6地址 R1配置 ipv6 #全局使能IPv6 interface GigabitEthernet0/0/0ipv6 enable ipv6 address auto link-local #为…

vue项目打包部署到服务器并使用cdn加速

配置 vue.config.js文件 const isProd process.env.NODE_ENV production module.exports {// 其他配置chainWebpack: config > {// 生产环境下使用CDNif (isProd) {config.plugin(html).tap(args > {args[0].cdn assetsCDNreturn args})}},// 生产环境下替换路径为c…

【Druid 登陆异常】

Druid 登陆异常 问题描述&#xff1a;页面登陆将请求参数放到请求体中导致无法通过request.getParameter 方式 服务加载流程 通过 DruidStatViewServletConfiguration 中方法 statViewServletRegistrationBean 初始化StatViewServlet 通过 com.alibaba.druid.support.jakarta…

【Android】在WSA安卓子系统中进行新实验性功能试用与抓包(2311.4.5.0)

前言 在根据几篇22和23的WSA抓包文章进行尝试时遇到了问题&#xff0c;同时发现新版Wsa的一些实验性功能能优化抓包配置时的一些步骤&#xff0c;因而写下此篇以作记录。 Wsa版本&#xff1a;2311.40000.5.0 本文出现的项目&#xff1a; MagiskOnWSALocal MagiskTrustUserCer…

Ultraleap 3Di示例Interactable Objects组件分析

该示例代码位置如下&#xff1a; 分析如下&#xff1a; Hover Enabled&#xff1a;悬停功能&#xff0c;手放在这个模型上&#xff0c;会触发我们手放在这个模型上的悬停功能。此时当手靠近模型的时候&#xff0c;手的模型的颜色会发生改变&#xff0c;反之&#xff0c;则不会…

Java服务端使用freemarker+wkhtmltoimage生成Echart图片

目录 1.通过 freemarker 将ftl转成html 1.1 freemarker 手册: 1.2 添加freemarker maven依赖 1.3 添加 echart-test.ftl 模版文件 1.4 添加 FreemarkerTool 工具类 1.5 添加测试main方法 1.6 运行,生成echart-test-时间戳.html 文件 2. 通过wkhtmltoimage将html 转为p…

数位dp,HDU 4151 The Special Number

一、题目 1、题目描述 In this problem, we assume the positive integer with the following properties are called ‘the special number’: 1) The special number is a non-negative integer without any leading zero. 2) The numbers in every digit of the special nu…

763. 划分字母区间 - 力扣(LeetCode)

题目描述 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。…

股票交易维度和概念

股票&#xff1a;股份公司为筹集资金而发行给各个股东作为持股凭证并借以取得股息和红利的一种有价证券 好处&#xff1a;分红、送股配股、交易收益、本金少、易变现、避免货币贬值 金融标的投资风险与收益 股票分类 蓝筹股 经营业绩长期稳定增长的大公司&#xff0c;一般是…

IaC基础设施即代码:Terraform 连接 azure Blob 实现多资源管理

目录 一、实验 1.环境 2.Terraform 连接 azure Blob 3.申请虚拟网络资源 4.申请子网资源 5.申请安全组资源 6.申请公网IP与网络接口资源 7.申请虚拟机资源 8.申请负载均衡器 9.销毁资源 二、问题 1.存储无法删除 一、实验 1.环境 &#xff08;1&#xff09;主机 表…

【mongoDB】文档 CRUD

目录 1.插入文档 批量插入&#xff1a; 2.查询文档 3.更新文档 4.删除文档 deleteOne() deleteMany() findOneAndDelete() 1.插入文档 可以使用 insert () 方法或者 save() 方法向集合中插入文档 语法如下&#xff1a; db.collection_name.insert(document) collectio…

6.2第三次作业

综合练习&#xff1a;请给openlab搭建web网站 网站需求&#xff1a; 1.基于域名www.openlab.com可以访问网站内容为welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料 和缴费网站&#xff0c;基于&#xff0c;www.openlab.com/data网站…