当前位置: 首页 > article >正文

[Java]微服务之分布式事务

介绍

下单业务,前端请求首先进入订单服务,创建订单并写入数据库。然后订单服务调用购物车服务和库存服务:

  • 购物车服务负责清理购物车信息
  • 库存服务负责扣减商品库存

问题分析:

  1. 下单过程中, 订单服务创建订单, 插入自己的数据库, 执行成功
  2. 购物车服务, 清理数据库中用户的购物车数据, 执行成功
  3. 库存服务扣减库存时, 商品库存不够, 那么库存服务就会抛出异常
  4. 订单创建成功了, 购物车页清理了, 但是库存却没有扣减
  5. 这就出现了事务的不一致

在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。

  1. 其中的每个服务的事务就是一个分支事务。
  2. 整个业务称为全局事务。

认识Seata

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

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

分布式事务总体解决思路

解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致。

  1. 需要引入事务协调者(全局事务)进行事务控制,
  2. 所有的子事务都要向全局事务汇报状态
  3. 如果子事务全部成功, 全局事务告知子事务, 提交数据库操作
  4. 如果存在失败的子事务, 全局事务告知子事务, 回滚数据库操作

Seata解决分布式事务的思考会更加完善

Seata事务管理中有三个重要的角色:

  1. TC(Transaction Coordinator)-事务协调者: 维护全局和分支事务的状态,协调全局事务提交或回滚
  2. TM (Transaction Manager)-事务管理器: 定义全局事务的范围、开始全局事务、提交或回滚全局事务
  3. RM(Resource Manager)-资源管理器: 管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态

部署TC服务

Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。

执行课前资料提供的《seata-tc.sql》,导入数据库表:

课前资料准备了一个seata目录,其中包含了seata运行时需要的所有配置文件:

  1. 其中yaml文件中的配置是我们需要关心的, 代码中有注释, 可以阅读, 需要时可以修改

  1. 我们将整个seata文件夹拷贝到虚拟机的/root目录:

Docker部署

  1. 服务之间都是采用容器名连接, 所欲要确保nacos、mysql都在hm-net网络中(同一个网络)。
  2. 查看网络列表, 看网络是否存在

  1. 查看容器所在的网络

  1. 如果某个容器不再hm-net网络,可以参考下面的命令将某容器加入指定网络:
docker network connect [网络名] [容器名]
  1. 在虚拟机的/root目录执行下面的命令:
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.1.97 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2

4.1 参数说明

  • --name: 指定容器名
  • -p 8099:8099: 微服务跟seata服务连接时的端口
  • -p 7099:7099: seata控制台访问端口
  • -e SEATA_IP: seata的IP地址, 要改成自己的
  • -v ./seata: 数据卷挂载, 让配置文件生效
  • --privileged=true: 本地文件授权
  • --network: 容器的网络
  • -d: 容器后台运行
  • seataio/seata-server:1.5.2: sreata镜像包和版本

4.2 执行说明

  1. 如果镜像下载困难,也可以把课前资料提供的镜像上传到虚拟机并加载

  1. 查看服务

http://192.168.1.97:8848/nacos

http://192.168.1.97:7099

账密: admin/admin

集成Seate

参与分布式事务的每一个微服务都需要集成Seata,我们以trade-service为例。

  1. 引入依赖

为了方便各个微服务集成seata,我们需要把seata配置共享到nacos,因此不仅仅要引入seata依赖,还要引入nacos依赖:

<!--统一配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>
  <!--seata-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  </dependency>

提示: cart-service服务/item-service服务/trade-service服务都需要改造

  1. 改造配置

首先在nacos上添加一个共享的seata配置,命名为shared-seata.yaml

  1. 配置项的说明

seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
      server-addr: 192.168.1.97:8848 # nacos地址
      namespace: "" # namespace,默认为空
      group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
      application: seata-server # seata服务名称
      username: nacos
      password: nacos
  tx-service-group: hmall # 事务组名称
  service:
    vgroup-mapping: # 事务组与tc集群的映射关系
      hmall: "default"

  1. 添加共享配置文件bootstrap.yaml

spring:
  application:
    name: trade-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.1.97:8848  # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置
          - dataId: shared-seata.yaml # 共享seata配置

  1. 改造SpringMvc配置文件
server:
  port: 8085 #每个微服务运行在不同端口
hm:
  swagger:
    title: "交易服务接口文档"
    package: com.hmall.trade.controller
  db:
    database: hm-trade

  1. 重启服务

JDK非11版本启动服务配置中加虚拟机选型, 不然服务启动报错

XA模式

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

seata的XA模式如下:

一阶段的工作:

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

二阶段的工作:

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

XA模式的优点是什么?

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

XA模式的缺点是什么?

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

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

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

  1. 给发起全局事务的入口方法添加@GlobalTransactional注解

  • 其他的分支事务, 因为有可能要订单回滚, 所以加一下事务注解

  1. 重启服务并测试: 提交订单, 看下单失败后, 购物车的数据能否回滚

  • 二阶段分支执行力回滚

AT模式

Seata主推的是AT模式,AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷

  1. 阶段一RM的工作:
  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态
  1. 阶段二提交时RM的工作:
  • 删除undo-log即可
  1. 阶段二回滚时RM的工作:
  • 根据undo-log恢复数据到更新前

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

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

实现AT模式

  1. 首先,添加资料中的seata-at.sql到微服务对应的数据库中

  1. 然后,修改application.yml文件,将事务模式修改为AT模式:

  1. 重新测试
  • 选择购物车的商品

  • 进行结算

  • 把商品数量改成0

  • 提交订单失败

  • 购物车数据回滚


http://www.kler.cn/a/415819.html

相关文章:

  • Vue:使用 KeepAlive 缓存切换掉的 component
  • hue 4.11容器化部署,已结合Hive与Hadoop
  • 【LeetCode: 145. 二叉树的后序遍历 + 栈】
  • qt QToolButton详解
  • 基于YOLOv8深度学习的智慧农业棉花采摘状态检测与语音提醒系统(PyQt5界面+数据集+训练代码)
  • C++ 【异步日志模块和std::cout << 一样使用习惯替代性好】 使用示例,后续加上远程日志
  • Redis【1】- 如何阅读Redis 源码
  • 3.22决策树,离散值
  • SpringMVC-Day2
  • layui-vue第三方库表格列事件怎么写
  • 人工智能之数学基础:欧式距离及在人工智能领域中的应用
  • 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(6)
  • 牛客 ZT13 小红的数字删除
  • go每日一题:mock打桩、defer、recovery、panic的调用顺序
  • 【Linux】进程控制-----进程等待wait与waitpid
  • 用Go语言重写Linux系统命令 -- ping
  • Python读取摄像头视频并将其保存为MP4文件
  • 利用Java爬虫获得店铺详情:技术解析
  • KUKA机器人中断编程5—自动回原点功能的编程
  • 工程企业如何做好成本控制?该如何入手?
  • 和鲸科技创始人CEO范向伟出席首届工业智算产业发展研讨会,共话 AI 创新与产业化落地
  • 在windows系统中安装python并确认安装成功
  • 中信建投张青:以金融智慧点亮公益新篇章
  • Flink的双流join理解
  • 一次完整的CNAS软件测试实验室内部审核流程
  • Ubuntu-20.04安装 terminator