微服务——分布式事务

news2024/11/13 10:34:15

事务理论基础

 

分布式锁保证多线程下数据库操作安全保障 分布式事务发生异常可以回滚.

使用postman发送请求插入一条新订单。

 然后现在库存只剩下8个商品,如果买10个的话应该统一失败。

CAP定理

假如node03在独立时将所有请求都堵塞并等待恢复和其余节点的连接的话以此保证数据一致性,就不满足可用性了。 

BASE理论

 

初识Seata

Seata的架构

部署TC服务

docker 部署

1.拉取镜像
docker pull seataio/seata-server:1.5.0
2.创建自定义文件映射数据卷
mkdir -p /docker_volume/seata
3.运行一个容器并拷贝数据卷目录出来
docker run --name seata-server -p 8091:8091 -d seataio/seata-server:1.5.0


docker cp seata-server:/seata-server  /docker_volume/seata

现在可以对application.yml配置文件进行自定义的修改配置。

3.删除已有容器
docker stop seata-server

docker rm seata-server
4.重新创建并运行容器
 docker run --name seata-server -p 8091:8091 -p 7091:7091 -v /docker_volume/seata/seata-server:/seata-server -e SEATA_IP=192.168.80.180 -e SEATA_PORT=8091 seataio/seata-server:1.5.0

接下来的配置之后再研究。

windows部署

1.使用提供的安装解压部署

2.解压后目录如下

3.修改配置文件
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 = "SH"
    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"
  }
}
4.在nacos添加配置

为使tc集群可以共享配置。使用nacos作为配置中心。因此服务端配置文件seataServer.properties文件需要在nacos中配好。

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

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.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
 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服务

微服务集成Seata

1.引入依赖

<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>
<!--seata starter 采用1.4.2版本-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>

2.修改配置文件

需要修改application.yml文件,添加一些配置:

seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    # 参考tc服务自己的registry.conf中的配置
    type: nacos
    nacos: # tc
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-tc-server # tc服务在nacos中的服务名称
      cluster: SH
  tx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称
  service:
    vgroup-mapping: # 事务组与TC服务cluster的映射关系
      seata-demo: SH

为三个服务分别配置之后重启服务。

然后可以看见对应的服务被seata发现了。

这里有个问题,就是用jdk17会报错,必须把idea配置的项目jdk变成jdk8,不是pom文件里面的jdk8.

 动手实践

XA模式

 第一阶段只是执行业务并返回结果并不提交,第二阶段才是提交。

如果第一阶段有的业务执行失败就会回滚之前提交的业务恢复状态。

 seata比起传统xa多了个TM做整个事务的注册和管理。

xa模式具有强一致性,但是第一阶段所有业务都不提交而是一直等待别的业务,会一直占用着锁,性能较差,可用性较低。

 实现

 这里还要再pom文件里把mysql依赖版本改成8.0.11才可以,不然会报错。并且还要再配置文件里面配置数据库连接时加上&serverTimezone=GMT%2B8(三个服务都要改)

执行一次正常下单之后,还剩下6个库存和600余额。

然后执行一次失败下单。这次抛出了异常。并且库存没有减少,余额也没有变化。

 看见扣款服务里面是有执行扣款的,但是却回滚了。

AT模式

与xa模式不同,at模式执行完sql之后会直接提交。因为没有等待,所以没有了资源锁定。

这种已经提交的数据库无法进行回滚,为了回退状态,会由RM记录数据快照用于将来回滚。

然后这里的二阶段再所有子事务都成功之后就只需要通知rn删除快照即可。存在子事务失败的话就基于快照进行回滚。

 

AT模式的脏写问题.

AT模式的阶段一和阶段二不是一个整体的锁定状态,会导致存在丢失更新的问题。

脏写问题解决方案

这个全局锁会记录是哪一个事务在操作哪一张表的哪一行数据,然后会进行锁定。

在解决方案中,事务1在获取全局锁之后只要不释放全局锁,别的事务2就无法获取来进行修改。会一直进行重试。

然后事务1要进行回滚要获取DB锁,但是这种形成了互相等待。seata的获取全局锁的重试最多等待时间为300ms,最终,事务2先释放了DB锁。

虽然这种解决方案感觉又梦回xa模式,但是XA的锁是数据库锁。AT的锁是TC记录的锁。两个锁的力度有很大差别。

TC锁是行锁并且对于其他不受seata管理的事务是不受影响。 

所以在极端情况下可能有别的事务也要来修改同一行数据。但是几率极低。

原因:

1.转账业务下99.9%的情况在第二阶段都是成功,不会进行回滚。

2.这种分布式事务本身业务耗时长,并发较低。

虽然概率低,AT模式还是对这种情况做了处理。

脏写问题解决方案的解决方案

seata在保存快照时保存了两个快照。一个是更新前,一个是更新后。

在进行回滚恢复的时候会拿更新后的快照和当前数据库数据进行对比,一样则说明没有别的事务修改,如果不一样seata就会判断无法恢复并发出警告。

 AT模式代码实现

注意:数据库连接池如果使用的是druid要使用druid-starter,不要使用druid。否则不生成undolog 无法回滚

 在TC服务用的数据库seata里面执行上面的sql文件新建lock_table表和undo_log表。

postman测试

发起一次失败的请求.

 

扣款完后发现库存不足,然后进行回滚,回滚完就删除快照。 ubdo_log里的数据用完就删,所以不打断点是看不见的。 

TCC模式

XA模式和AT模式都会加锁,都会存在性能的损耗。

AT模式损耗服务器性能,TCC模式损耗我的性能,给爷整笑了。

每个不同的事务在阶段一冻结的是不同资源,后面阶段二里提交或者回滚也都是动用先前冻结的资源.

TCC模式代码实现 

 这里只需要改成扣款的服务,其订单服务不适合TCC改造。扣减库存的也可以用TCC进行改造。

这里要先在数据库里添加可用金额的字段。

幂等性:一个接口调用多少次效果都是一致的。

空回滚和业务悬挂

两个子事务,一个try成功,一个阻塞超时了,TM发出回滚的命令,但是其中一个都没有try,回滚会有问题。如果报错的话,seata会重试cancel。

 在空回滚之后业务恢复了,继续执行try,但是此时已经没有阶段二了。

因此try和cancel要做互相的判断再执行。

因此需要在数据库里记录事务的当前状态。

还要使用给的sql文件进行表格的创建.

@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper freezeMapper;
    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //0.获取事务id
        String xid = RootContext.getXID();
        //1.判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,要拒绝业务。
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if(oldFreeze==null){
            //CANCEL执行过,要拒绝业务
            return;
        }
        //1.扣减可用金额
        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);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //1.获取事务id
        String xid = ctx.getXid();
        //2.根据id删除冻结记录
        int count = freezeMapper.deleteById(xid);
        return count==1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        //0.查询冻结记录
        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();
        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);
            return true;
        }
        //2.幂等判断
        if(freeze.getState()==AccountFreeze.State.CANCEL)
        {
            //已经处理一次CANCEl了,无需重复处理
            return  true;
        }
        //1.恢复可用余额
        accountMapper.refund(freeze.getUserId(),freeze.getFreezeMoney());
        //2.将冻结金额清零,状态给为CANCEL
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count==1;
    }
}

上面就是代码实现,测试的时候要在controller层里面修改调用的service接口

正常情况

异常情况

下单数量远超库存,此时业务会失败,集体回滚,数据库数据都不变.

 freeze表里也会有一条记录。

控制台也显示用的TCC模式 

SAGA模式

总结对比


高可用

这个就不做了,感觉以后没什么机会做这个。

 

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

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

相关文章

【Spring Boot】快速入门

一、引言 1、什么是spring boot&#xff1f; Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xff…

【c++】stl_priority_queue优先级队列

目录 一、priority_queue的介绍 二、 priority_queue的本质 三、priority_queue的使用 四、priority_queue的模拟实现 总结 一、priority_queue的介绍 首先让我们通过阅读优先级队列的官方文档 简单翻译一下 1. 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准…

内存不够用怎么办?整理了几个必备的方法!

内存越大&#xff0c;运行越快&#xff0c;程序之间的切换和响应也会更加流畅。但是随着时间的增加&#xff0c;还是堆积了越来越多的各种文件&#xff0c;导致内存不够用&#xff0c;下面就像大家介绍三种好用的清理内存的方法。 方法一&#xff1a;通过电脑系统自带的性能清理…

关于Android studio新版本和NEW UI显示返回按钮的设置

1.新版Android studio问题 因为在新版本的Android Studio中&#xff0c;默认情况下是没有直接的选项来显示返回上一步按钮在状态栏上的&#xff0c;可以通过以下方法来实现返回上一步的功能&#xff1a; 在Android Studio的顶部菜单栏中&#xff0c;选择"View"。在…

量子纠错率提高100倍!亚马逊云科技开启量子创新时代

由AWS开发和制造的包装组件中的超导量子芯片&#xff08;图片来源&#xff1a;网络&#xff09; 作为一项尖端技术&#xff0c;量子计算能提供前所未有的计算能力。美国亚马逊云科技&#xff08;AWS&#xff09;近期推出了一款量子芯片&#xff0c;展示出该技术取得了重大飞跃…

为什么选择国产WordPress:HelpLook的优势解析

如今网站建设可以说已经是企业必备。而在众多的网站建设工具中&#xff0c;WordPress无疑是其中的佼佼者。作为一款开源的CMS&#xff08;内容管理系统&#xff09;&#xff0c;WordPress拥有丰富的插件和主题&#xff0c;以及强大的功能&#xff0c;使得用户可以轻松地构建出符…

golang 操作Jenkins

1.創建Agent/Node func CreateAgent(username string, password string, nodeName string, nodeDescription string, numExecutors string, remoteFS string, labelString string, host string) {var obj stringobj "{name:" nodeName ",nodeDescription:&q…

洛谷 P8802 [蓝桥杯 2022 国 B] 出差

文章目录 [蓝桥杯 2022 国 B] 出差题目链接题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 思路解析CODE [蓝桥杯 2022 国 B] 出差 题目链接 https://www.luogu.com.cn/problem/P8802 题目描述 A \mathrm{A} A 国有 N N N 个城市&#xff0c;编号为 1 … N …

抖音跑腿小程序开发指南:从零开始到上线

如今&#xff0c;抖音跑腿小程序的开发已经成为一项具有巨大潜力的领域。本文将为您提供一份详尽的开发指南&#xff0c;从零开始引导您完成一个成功的抖音跑腿小程序的开发和上线过程。 第一步&#xff1a;确定目标和需求 了解用户的期望&#xff0c;确定小程序的功能模块&a…

ROS-ROS通信机制-小乌龟

文章目录 1.话题发布2.话题订阅3.服务调用4.参数设置5.通信机制比较 1.话题发布 需求描述: 编码实现乌龟运动控制&#xff0c;让小乌龟做圆周运动。 实现分析: 乌龟运动控制实现&#xff0c;关键节点有两个&#xff0c;一个是乌龟运动显示节点 turtlesim_node&#xff0c;另…

在RHEL8中如何使用 SELinux

本章主要介绍在RHEL8中如何使用 SELinux。 了解什么是 SELinux 了解 SELinux 的上下文 配置端口上下文 了解SELinux的布尔值 了解SELinux的模式 在 Windows系统中安装了一些安全软件后&#xff0c;当执行某个命令时&#xff0c;如果安全软件认为这个 命令对系统是一种危害&a…

【广州华锐互动】3D虚拟还原井下复杂事故:提高安全意识,预防事故再次发生

随着科技的不断发展&#xff0c;3D虚拟现实技术已经逐渐应用于各个领域&#xff0c;为我们的生活带来了诸多便利。在钻井行业&#xff0c;3D虚拟现实技术的应用也日益受到重视。通过3D虚拟还原井下复杂事故&#xff0c;可以帮助我们更直观地了解事故发生的原因和过程&#xff0…

Python3开发环境的搭建

1&#xff0c;电脑操作系统的确认 我的是win10、64位的&#xff0c;你们的操作系统可自寻得。 2&#xff0c;Python安装包的下载 &#xff08;1&#xff09;浏览器种输入网址&#xff1a;https://www.python.org 选择对应的系统&#xff08;我的是win10/64位) &#xf…

如何精准找企业线索?你缺一个法宝

如何找企业数据、找客户&#xff1f;近年来&#xff0c;经常会听到认识的销售朋友抱怨&#xff0c;每天花费大量时间从各种渠道找企业信息、找客户线索&#xff0c;结果总是不如意&#xff0c;投入大量时间&#xff0c;产出却0&#xff1b;创业成功的老板也抱怨优秀人才越来越难…

CSS特效027:超可爱的小狗狗,女孩子都喜欢

为什么当初选择计算机行业&#xff1f; 科技总是迷人的&#xff0c;通过计算机可以造出各种美妙的设计。 后来工作&#xff0c;更加感觉到可视化的魅力&#xff0c;于是乎更加的专注于canvas&#xff0c;CSS&#xff0c;openlayers&#xff0c;leaflet&#xff0c;cesium。。。…

Axure系列(二)之 元件库的介绍以及个人简介案例展示

目录 一. 元件介绍 二. 基本元件的使用 2.1 形状元件 2.2 图片元件 2.3 占位符 2.4 文本 2.5 线段元件 2.6 热区文件 三. 表单元件的使用 3.1 文本框 3.2 文本域 3.3 下拉列表 3.4 列表框 3.5 复选框 3.6 单选按钮 四. 菜单与表格元件的使用 4.1 树 4.2 表格…

大模型元年压轴盛会定档12月28日,第十届WAVE SUMMIT即将启航

回望2023年&#xff0c;大语言模型或许将是科技史上最浓墨重彩的一笔。从技术、产业到生态&#xff0c;大语言模型在突飞猛进中加速重构万物。随着理解、生成、逻辑、记忆四大能力显著提升&#xff0c;大语言模型为通用人工智能带来曙光。 AI开发者们正在用算法和代码书写一个…

连连看游戏

连通块记忆性递归的综合运用 这里x&#xff0c;y的设置反我平常的习惯&#xff0c;搞得我有点晕 实际上可以一输入就交换x&#xff0c;y的数据的 如果设置y1为全局变量的话会warning&#xff1a; warning: built-in function y1 declared as non-function 所以我改成p和q了…

gin投票项目5

对应视频V3版本 1.优化用户注册的功能 增加扩展字段 1.增加一个UUID字段&#xff0c;vachar&#xff08;50&#xff09;。 2.增加一个UUID的唯一索引。 UUID具有全局唯一性&#xff1b; 方法&#xff1a;在数据库中新建一个列&#xff0c;名为uuid并移至主键下方&#xf…

CTF-misc(1)图片隐写

笔记目录 渗透测试工具(1)wireshark渗透测试工具(2)Nmap渗透测试工具(3)BurpsuiteAWD比赛(1)AWD入门攻略大纲CTF-Web(2)SQL注入CTF-Web(3)文件上传漏洞 图片隐写目录 (1)GIf和二维码隐写 二维码补全 二维码绘图 Gif规律分析 (2)文本附加图片隐写 (3)IHDR文件头修复图片宽高 (…