SpringCloud学习笔记(五)

8.Seata分布式事务

8.1. Seata简介

  • Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

8.2. Seata工作组件

  • XID:全局事务的唯一标识,在微服务调用链中传递,绑定到服务的事务的上下文。
  • TC:事务协调者,就是Seata,维护全局和分支事务的状态,驱动全局事务提交或回滚。(以Seata Server的形式独立部署)
  • TM:事务管理器,标注全局@GlobalTransactional启动入口动作的微服务模块,是事务的发起者。定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM:资源管理器,是MySQL数据库本身,管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

8.3. Seata工作流程

1.TM向TC申请开启全局事务,创建成功生成全局唯一的XID。

2.XID在微服务调用链中传播。

3.RM向TC注册分支事务,将其纳入XID对应的全局事务的管辖。

4.TM向TC发起针对XID的全局提交或回滚决议。

5.TC调度XID下管辖的全部分支事务完成提交或回滚请求。

8.4. Seata安装

8.4.1.安装流程
  • 下载解压

    • https://github.com/seata/seata/releases
  • 创建数据库

    • seata的server端需要创建一个数据库,用于存储事务信息
CREATE DATABASE seata;
USE seata;
  • 导入表

    • https://github.com/apache/incubator-seata/blob/2.x/script/server/db/mysql.sql

    • 执行sql脚本

  • 修改配置文件

    • 修改application.yml
#  Copyright 1999-2019 Seata.io Group.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.server:port: 7091spring:application:name: seata-serverlogging:config: classpath:logback-spring.xmlfile:path: ${log.home:${user.home}/logs/seata}extend:logstash-appender:destination: 127.0.0.1:4560kafka-appender:bootstrap-servers: 127.0.0.1:9092topic: logback_to_logstashconsole:user:username: seatapassword: seata
seata:config:# support: nacos, consul, apollo, zk, etcd3type: nacosnacos:server-addr: 127.0.0.1:8848namespace: ""group: SEATA_GROUPusername: nacospassword: nacosregistry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPnamespace: ""cluster: defaultusername: nacospassword: nacosstore:# support: file 、 db 、 redis 、 raftmode: dbdb:datasource: druiddb-type: mysql driver-class-name: com.mysql.dj.jdbc.Driverurl: jdbc:mysql://localhost:3307/seata?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=trueuser: rootpassword: 123456minConn: 10maxConn: 100global-table: global_tablebranch-table: branch_tablelock-table: lock_tabledistributed-lock-table: distributed_lockquery-limit: 1000maxWait: 5000#  server:#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'security:secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017tokenValidityInMilliseconds: 1800000ignore:urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**
  • 启动nacos

  • 启动seata

    • bin目录下执行seata-server.sh
8.4.2.验证
  • 访问nacos,服务注册成功

  • 访问seata,账号密码初始化均为seata,登录成功

8.5. Seata使用(订单下单案例)

1.创建三个数据库

CREATE DATABASE seata_order;
CREATE DATABASE seata_storage;
CREATE DATABASE seata_account;

2.创建表

3.mybatis一键生成实体和标准mapper

4.创建三个微服务

  • 订单服务
  • 库存服务
  • 账户服务

5.新增库存和账户两个Feign接口

  • 库存接口
@FeignClient(name = "seata-storage-service")
public interface StorageFeignApi {/*** 扣减库存*/@PostMapping("/storage/decrease")ResultData decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
  • 账户接口
@FeignClient(name = "seata-account-service")
public interface AccountFeignApi {/*** 扣减账户余额*/@PostMapping("/account/decrease")ResultData decrease(@RequestParam("userId") Long userId, @RequestParam("money") Long money);
}

6.订单微服务

  • 配置文件
server:port: 2001spring:application:name: seata-order-servicecloud:nacos:server-addr: localhost:8848datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/seata_order?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: rootpassword: 123456mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: org.example.cloud.entitiesconfiguration:map-underscore-to-camel-case: trueseata:registry:type: nacosnacos:server-addr: localhost:8848namespace: ""group: SEATA_GROUPapplication: seata-servertx-service-group: default_tx_group # 事务组名称,由他获得TC服务的集群名称service:vgroup-mapping:# 事务组名称与集群名称的映射关系,例如当前事务组为ProjectA,这个ProjectA的集群名称为default,如果当前集群down了,只需要修改集群名称即可启动备用集群进行事务管理default_tx_group: default data-source-proxy-mode: ATlogging:level:io:seata: info
  • 业务代码
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderMapper orderMapper;@Resource// 订单微服务调用库存微服务private StorageFeignApi storageFeignApi;@Resource// 订单微服务调用账户微服务private AccountFeignApi accountFeignApi;@Overridepublic void create(Order order) {//xid全局事务id检查String xid = RootContext.getXID();//1. 新建订单log.info("----->开始新建订单: " + "\t" + "xid: " + xid);// 订单新建初始状态为0order.setStatus(0);int result = orderMapper.insertSelective(order);Order orderFromDB = null;if(result > 0) {// 从mysql查出记录orderFromDB = orderMapper.selectOne(order);log.info("----->新建订单成功: " + "\t" + "orderFromDB info: " + orderFromDB);System.out.println();log.info("----->调用storage: " + "\t");storageFeignApi.decrease(orderFromDB.getProductId(), orderFromDB.getCount());log.info("----->调用storage完成: " + "\t");log.info("----->调用account: " + "\t");accountFeignApi.decrease(orderFromDB.getUserId(), order.getMoney());log.info("----->调用account完成: " + "\t");System.out.println();// 修改订单状态,从0改为1log.info("----->修改订单状态: " + "\t");orderFromDB.setStatus(1);Example whereCondition = new Example(Order.class);Example.Criteria criteria = whereCondition.createCriteria();criteria.andEqualTo("userId", order.getId());criteria.andEqualTo("status", 0);int updateResult = orderMapper.updateByExampleSelective(orderFromDB, whereCondition);log.info("----->修改订单状态完成: " + "\t" + "updateResult: " + updateResult);log.info("----->orderFromDB info: " + orderFromDB);}System.out.println();System.out.println("----->结束新建订单: " + "\t" + "xid: " + xid);}
}

7.库存微服务

  • mapper
public interface StorageMapper extends Mapper<Storage> {void decrease(@Param("productId") Long productId, @Param("count") Integer count);
}
<update id="decrease">update t_storageset used = used + #{count},residue = residue - #{count}where product_id = #{productId}
</update>

8.账户微服务

  • mapper
public interface AccountMapper extends Mapper<Account> {void decrease(@Param("userId") Long userId, @Param("money") Long money);
}
<update id="decrease">update t_accountset used = used + #{money},residue = residue - #{money}where user_id = #{userId}
</update>

8.6. Seata测试(订单下单案例)

  • 未添加@GlobalTransactional注解

    • 账户微服务超时异常
      • 账户微服务和库存微服务均未回滚
      • 订单状态为0,未进行更新
    • 账户微服务业务代码异常
      • 账户微服务和库存微服务均未回滚
      • 订单状态为0,未进行更新
// OrderServiceImpl.java
@GlobalTransactional(name = "zzyy-create-order", rollbackFor = Exception.class)
public void create(Order order) {//xid全局事务id检查String xid = RootContext.getXID();//1. 新建订单log.info("----->开始新建订单: " + "\t" + "xid: " + xid);// 订单新建初始状态为0order.setStatus(0);int result = orderMapper.insertSelective(order);Order orderFromDB = null;if(result > 0) {// 从mysql查出记录orderFromDB = orderMapper.selectOne(order);log.info("----->新建订单成功: " + "\t" + "orderFromDB info: " + orderFromDB);System.out.println();log.info("----->调用storage: " + "\t");storageFeignApi.decrease(orderFromDB.getProductId(), orderFromDB.getCount());log.info("----->调用storage完成: " + "\t");log.info("----->调用account: " + "\t");accountFeignApi.decrease(orderFromDB.getUserId(), orderFromDB.getMoney());log.info("----->调用account完成: " + "\t");System.out.println();// 修改订单状态,从0改为1log.info("----->修改订单状态: " + "\t");orderFromDB.setStatus(1);Example whereCondition = new Example(Order.class);Example.Criteria criteria = whereCondition.createCriteria();criteria.andEqualTo("id", orderFromDB.getId());criteria.andEqualTo("status", 0);int updateResult = orderMapper.updateByExampleSelective(orderFromDB, whereCondition);log.info("----->修改订单状态完成: " + "\t" + "updateResult: " + updateResult);log.info("----->orderFromDB info: " + orderFromDB);}System.out.println();System.out.println("----->结束新建订单: " + "\t" + "xid: " + xid);
}
  • 添加@GlobalTransactional注解
    • 账户微服务超时异常
      • 超时前,账户微服务和库存微服务均未回滚,逻辑正常更新,但订单状态为0,未进行更新,且undolog记录存在

      • seata后台

      • 超时后,账户微服务和库存微服务均回滚,订单记录消失,undolog记录被删除

      • seata后台

8.7.原理总结与面试题

8.7.1 AT模式如何做到对业务无侵入

1.两阶段提交协议的演变

  • 一阶段:业务数据和回滚日志记录在同一个事务中提交,释放数据库锁资源。

  • 二阶段:

    • 提交异步化,非常快速地完成。
    • 基于undo_log回滚日志,反向补偿。

2.一阶段加载

在一阶段,Seata会拦截业务SQL,
(1)解析SQL语义,找到业务SQL要更新的业务数据,在业务数据被更新前,将其保存成“before image”,
(2)执行业务SQL更新业务数据,在业务数据更新之后,
(3)将其保存成“after image”,最后生成行锁。
上述操作在一个数据库事务完成,保证原子性。

3.二阶段提交

  • 二阶段如果是顺利提交,因为业务SQL在一阶段已经提交,所以Seata只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
  • 二阶段如果是回滚的话,Seata需要回滚一阶段已经执行的业务SQL,回滚的方式就是用“before image”还原业务数据,还原之前要首先校验脏写,对比“数据库当前业务数据”和“after image”,如果数据不一致,说明有脏写,需要转人工处理,如果数据一致,则用“before image”去覆盖“当前业务数据”,完成数据回滚。最后清理掉一阶段保存的快照数据和行锁。

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

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

相关文章

(14)gdb 笔记(7):以日志记录的方式来调试多进程多线程程序,linux 命令 tail -f 实时跟踪日志

&#xff08;44&#xff09;以日志记录的方式来调试多进程多线程程序 &#xff1a; 这是老师的日志文件&#xff0c;可以用来模仿的模板&#xff1a; &#xff08;45&#xff09;实时追踪日志的 tail -f 命令&#xff1a; &#xff08;46&#xff09; 多种调试方法结合起来用 …

视觉硬件选型和算法选择(CNN)

基础知识 什么是机械视觉: 机械视觉是一种利用机器代替人眼来进行测量和判断的技术&#xff0c;通过光学系统、图像传感器等设备获取图像&#xff0c;并运用图像处理和分析算法来提取信息&#xff0c;以实现对目标物体的识别、检测、测量和定位等功能。 机械视觉与人类视觉有什…

尝试一下,交互式的三维计算python库,py3d

py3d是一个我开发的三维计算python库&#xff0c;目前不定期在PYPI上发版&#xff0c;可以通过pip直接安装 pip install py3d 开发这个库主要可视化是想把自己在工作中常用的三维方法汇总积累下来&#xff0c;不必每次重新造轮子。其实现成的python库也有很多&#xff0c;例如…

【C++高并发服务器WebServer】-15:poll、epoll详解及实现

本文目录 一、poll二、epoll2.1 相对poll和select的优点2.2 epoll的api2.3 epoll的demo实现2.5 epoll的工作模式 一、poll poll是对select的一个改进&#xff0c;我们先来看看select的缺点。 我们来看看poll的实现。 struct pollfd {int fd; /* 委托内核检测的文件描述符 */s…

接入 deepseek 实现AI智能问诊

1. 准备工作 注册 DeepSeek 账号 前往 DeepSeek 官网 注册账号并获取 API Key。 创建 UniApp 项目 使用 HBuilderX 创建一个新的 UniApp 项目&#xff08;选择 Vue3 或 Vue2 模板&#xff09;。 安装依赖 如果需要在 UniApp 中使用 HTTP 请求&#xff0c;推荐使用 uni.requ…

PLSQL: 存储过程,用户自定义函数[oracle]

注意: raise notice是高斯的输出语句; DBMS_OUT_PUT.PUT_LINE是oracle的输出语句 存储过程 Stored Procedure 存储过程可以封装数据访问逻辑&#xff0c;使得应用程序可以通过调用存储过程来执行这些逻辑&#xff0c;而不是直接执行SQL语句。这有助于提高代码的可重用性、可…

从零开始玩转Docker:轻松开启容器化之旅

一、什么是 Docker Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。简单来说&#xff0c;Docker 就像是一个超级 “快递箱”&#xff0c…

deepseek+kimi自动生成ppt

打开deepseek官网&#xff0c;输入详细的需求&#xff0c;让他生成个ppt 接着deepseek开始思考生成了 接着复制生成了的内容 打开kimi粘贴刚才deepseek生成的内容 可以一键生成啦&#xff0c;下载编辑使用吧

单张照片可生成写实3D头部模型!Adobe提出FaceLift,从单一的人脸图像中重建出360度的头部模型。

FaceLift是Adobe和加州大学默塞德分校推出的单图像到3D头部模型的转换技术,能从单一的人脸图像中重建出360度的头部模型。FaceLift基于两阶段的流程实现:基于扩散的多视图生成模型从单张人脸图像生成一致的侧面和背面视图;生成的视图被输入到GS-LRM重建器中,产出详细的3D高斯表…

机器学习:朴素贝叶斯分类器

贝叶斯决策论是概率框架下实施决策的基本方法,对分类任务来说,在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。 贝叶斯定理是贝叶斯决策论的基础&#xff0c;描述了如何根据新的证据更新先验概率&#xff0c;贝叶斯定理&…

5 计算机网络

5 计算机网络 5.1 OSI/RM七层模型 5.2 TCP/IP协议簇 5.2.1:常见协议基础 一、 TCP是可靠的&#xff0c;效率低的&#xff1b; 1.HTTP协议端口默认80&#xff0c;HTTPSSL之后成为HTTPS协议默认端口443。 2.对于0~1023一般是默认的公共端口不需要注册&#xff0c;1024以后的则需…

excel合并表格

上一章说到excel拆分表格&#xff0c;可以按一列的不重复数据自动拆分成多个表格。这个功能主要适用于有多个下级机构的部门分发表格使用。表格分发完成&#xff0c;下级单位修改后&#xff0c;上传到我们这里。我们还得把这些表格合并成一个表。如果利用复制粘性&#xff0c;工…

区块链100问之加密算法

区块链100问之加密算法 文章目录 区块链100问之加密算法哈希算法是什么&#xff1f;有什么特征&#xff1f;哈希碰撞是什么?雪崩效应呢&#xff1f;如何解决&#xff1f;哈希算法的作用&#xff1f;对称加密和非对称加密有什么区别&#xff1f;为什么会引入非对称加密&#xf…

HTML应用指南:利用GET请求获取全国盒马门店位置信息

随着新零售业态的发展,门店位置信息的获取变得至关重要。作为新零售领域的先锋,盒马鲜生不仅在商业模式创新上持续领先,还积极构建广泛的门店网络,以支持其不断增长的用户群体。本篇文章,我们将继续探究GET请求的实际应用,我们使用Python的requests库通过GET请求,从盒马…

(原创,可用)SSH实现内外网安全穿透(安全不怕防火墙)

目前有A、B终端和一台服务器&#xff0c;A、B机器不能直接访问&#xff0c;服务器不能直接访问A、B终端但是A、B终端可以访问服务器&#xff0c;这个场景很像我们有一台电脑在单位内网&#xff0c;外机器想访问内网系统&#xff0c;可能大家目前想到的就是frp之类穿透工具&…

keil5显示[NO J-link found]解决办法——【J-LINK】驱动下载安装教程

打开电脑的设备管理器&#xff0c;检查通用串行总线控制器 看这里发现没有J-link driver&#xff0c;于是开始安装下驱动。 一、下载 打开J-link下载官网链接&#xff1a; SEGGER - The Embedded Experts - Downloads 点击左下角下载按钮 二、安装 等待下载完成以后双击下载…

Maven的dependencyManagements锁定版本依赖

Pom工程的父项目定义的依赖 <dependencyManagement><dependencies><!--springcloud 2023.0.0--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version&g…

Coze(扣子)+ Deepseek:多Agents智能体协作开发新范式

前言 在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术的迅猛发展正深刻改变着我们的生活和工作方式。从智能语音助手到自动化流程机器人&#xff0c;AI 的应用无处不在&#xff0c;为我们提供了更加便捷、高效的服务。然而&#xff0c;对于非专业人士来…

【文献讲解】《Non-local Neural Networks》

一、引言 传统的深度学习方法(如卷积神经网络CNN和循环神经网络RNN)在捕捉长距离依赖关系时存在局限性。CNN主要关注局部邻域的特征,而RNN则依赖于序列的递归计算,无法直接捕捉全局信息。为了解决这一问题,本文提出了一种非局部神经网络(Non-local Neural Networks),通…

BiGRU双向门控循环单元多变量多步预测,光伏功率预测(Matlab完整源码和数据)

代码地址&#xff1a;BiGRU双向门控循环单元多变量多步预测&#xff0c;光伏功率预测&#xff08;Matlab完整源码和数据) BiGRU双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 一、引言 1.1、研究背景和意义 随着全球对可再生能源需求的不断增长&#xff0c;光伏…