【SpringCloud】使用Seata实现分布式事务

news2024/11/16 6:31:27

目录

    • 一、Seata 框架的需求背景
    • 二、Seata 事务模式与架构
      • 2.1 Seata 组成
      • 2.2 Seata 事务模式
    • 三、Seata 实战演示
      • 3.1 部署 Seata Server
        • 3.1.1 下载 Seata Server
        • 3.1.2 更改 Seata Server 配置
        • 3.1.3 创建 Seata Server 所需的数据库、数据库表
        • 3.1.4 启动 Seata Server
      • 3.2 Seata AT 模式分布式事务数据一致性演示
        • 3.2.1 添加依赖
        • 3.2.2 在项目中配置 Seata
        • 3.2.2 项目中使用 Seata AT 模式

一、Seata 框架的需求背景

一般情况下,在 Java 项目中如果存在对一个数据库实例的一系列操作的,为了避免程序执行过程中出现异常,我们通常会使用一个事务包裹住这一系列数据库操作,从而保证这一组操作的数据安全。我们通常使用Spring工具箱中的工具来实现本地事务(例如 @Transactional 注解提供的声明式事务功能等)。
但是,在微服务架构下,一个微服务可能存在多个实例,或者说一个微服务实例在运行期间可能同时对多个数据库实例进行CRUD。在这种分布式应用场景下,使Spring工具箱内的工具就失效了。我们需要借助分布式事务来保证分布式应用场景下的数据一致性。Seata 提供了分布式事务完备的解决方案,针对不同的分布式事务场景有不同的模式。

二、Seata 事务模式与架构

2.1 Seata 组成

Seata 框架由三个组件组成,分别是事务协调器(Transaction Coordinator,简称TC),事务管理器(Transaction Manager,简称TM),资源管理器(Resource Manager,简称RM)。

  • 事务协调器(TC):是一个中心化的事务协调者的角色,主要用来协调全局事务的提交和回滚,并管理全局事务与分支事务的状态。是一个需要独立部署的服务,也就是我们下面要进行部署的 Seata Server;
  • 事务管理器(TM):主要用来发起一个全局事务,对一个全局事务是提交,还是回滚做出决议。在我们实际的项目中,
  • 资源管理器(RM):操作分支事务进行提交或者回滚,并向事务协调器(TC)上报分支事务的状态。

在这里插入图片描述
以上图为例来介绍下 Seata 的架构。 事务协调器(TC,又被叫做 Seata Server)是需要被独立部署的,在AT 模式下,TM 和 RM 则是在相应的微服务中引入依赖,添加相应的配置,在需要发起全局事务的方法上添加相应的注解即可。

  • 上图在 Business 微服务模块中发起一个全局事务,并将该全局事务注册到 TC 上;
  • Business 微服务模块调用了 Stock 微服务和 Order 微服务,Stock 微服务和 Order 微服务都属于该全局事务下的分支事务,这2个被调用的微服务执行本地事务并生成相应的回滚日志到undolog数据库表中,执行完成之后(无论成功或失败)也会向 TC 上报分支事务执行情况。
  • 在 Order 微服务中又调用了 Account 微服务,又开启了一个分支事务,Account 微服务执行本地事务并生成相应的回滚日志到undolog数据库表中,执行完毕之后向 TC 上报自己所在的分支事务执行状态。
  • Business 微服务对全局事务的执行结果进行决议,如果所有的分支事务都执行成功,则提交全局事务,否则,会根据undolog数据库表中记录的回滚日志,对所有执行成功的分支事务进行回滚。

2.2 Seata 事务模式

模式类型数据一致性性能业务侵入性适用场景
AT 模式弱一致性无侵入大多数分布式事务场景,要求无须改造业务代码,快速接入时
XA 模式强一致性无侵入想要迁移到 Seata 平台基于 XA 协议的老应用,使用 XA 模式将更平滑
Saga 模式最终一致性很高有侵入,需要实现状态机及补偿代码业务流程长、业务流程多,或者遗留系统服务,无法提供TCC要求的3个接口
TCC 模式弱一致性很高有侵入,需要实现try,confirm,cancel 3个接口适用于核心系统等对性能有很高要求的场景

三、Seata 实战演示

下面演示 AT 模式下的 Seata 使用。分为2各部分:部署 Seata Server,分布式事务的数据一致性。

3.1 部署 Seata Server

3.1.1 下载 Seata Server

在 Seata 的 Github 主页的 Release 页面下载对应版本的二进制压缩包。
在这里插入图片描述

3.1.2 更改 Seata Server 配置

默认的 Seata Server 无法运行,需要更改配置,这里的 Seata Server 将使用 MySQL 数据库作为数据源,使用 Nacos 作为服务发现、注册中心。
首先,在Seata Server 的conf 目录下找到文件 file.conf.example 复制一份,并将文件名改为 file.conf,打开该文件进行更改,改动主要集中在 store 节点:

store {
  ## store mode: file、db
  mode = "db"
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "druid"
    ## 配置的数据库类型
    dbType = "mysql"
    ## 数据库连接驱动
    driverClassName = "com.mysql.jdbc.Driver"
    ## 数据库连接 URL
    url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
    ## 数据库连接账号密码
    user = "fuyou"
    password = "fuyou3323"
    ## 数据库连接配置
    minConn = 5
    maxConn = 50
    ## Seata Server在数据库表中存放全局事务锁信息的表名
    globalTable = "global_table"
    ## Seata Server在数据库表中存放分支事务锁信息的表名
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  }
}

以上配置的主要更改点是,把mode字段取值改为db,然后就是数据库相关的配置,设置数据库类型,数据库驱动,数据库连接的URL,数据库的账号密码,以及 Seata Server 存储全局锁及表所的数据库表名。

接下来接着更改 registry.conf 文件里的配置,内容大概如下:

registry {
  # 支持 file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "myGroup"
    namespace = "public"
    cluster = "default"
    username = ""
    password = ""
  }
}

因为我们使用 Nacos 进行服务的注册发现,因此我们将Seata Server也注册到 Nacos 上进行服务管理和使用(当然,你也可以选择其他类型的注册中心)。上述配置主要是配置了 注册中心的类型,Seata Server的服务名,Nacos的地址、服务组等信息。

3.1.3 创建 Seata Server 所需的数据库、数据库表

我们在上面配置了 Seata Server 中使用MySQL数据库进行存储,并在 URL 中配置了 Seata Server 使用的数据库库名为 seata。Seata Server(TC)在运行期间,需要管理协调全局事务和分支事务,在这个过程需要把一些信息存在数据库中,例如XID(全局事务ID),分支事务ID等信息。
为了避免资源的竞争,Seata也会为资源加锁,避免不同的全局事务之间抢占资源。例如,一个分支事务只有在获取到全局锁和本地锁的情况下才可以提交成功对应的分支事务,并将对应的回滚日志写到数据库中。全局锁和本地锁的相关信息就存在名为 seata 的数据库的 lock_table 表中。

-- 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 = InnoDB
    DEFAULT CHARSET = utf8mb4;

-- 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 = InnoDB
    DEFAULT CHARSET = utf8mb4;

-- 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 = InnoDB
    DEFAULT CHARSET = utf8mb4;

本地创建库表情况如下:
在这里插入图片描述

除了 Seata Server 要使用的数据库及数据库表以外,还需要在引入 Seata 依赖的 client 侧的微服务所使用的数据库内,创建一个数据库表undo_log,这个就是 Seata 的回滚日志表,每个执行成功的分支事务都需要将该分支对应的回滚日志写入到该表中,如果全局事务回滚时,每个分支事务就根据分支事务的回滚日志进行恢复。

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 = InnoDB
    AUTO_INCREMENT = 1
    DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
3.1.4 启动 Seata Server

在解压好的Seata Server 目录内,切换到 /bin 目录下,运行 Seata Server 的启动脚本。
在这里插入图片描述
启动成功后,效果大致如下:

在这里插入图片描述

PS :
1. 因为 Seata 要注册到 Nacos 上,所以在启动Seata Server之前务必先确保Nacos服务在线(你也可以选择其他类型的注册中心,因为我使用的是Nacos,所以需要确保Nacos在线);
2. 尽量确保 seata server 的 /lib 目录内依赖的mysql 驱动的版本与本地安装的数据库版本一致,或者相近,否则可能会有报错;

3.2 Seata AT 模式分布式事务数据一致性演示

3.2.1 添加依赖

接下来我们要演示 Seata 的 AT 模式下可以保证分布式事务的数据一致性。对于 Seata 来说微服务侧是 client 端,我们首先需要先添加 Seata 依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
3.2.2 在项目中配置 Seata

在需要使用Seata的每个微服务的 application.yaml 文件中添加 Seata 的配置项:

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: seata-server-group

seata:
  application-id: order
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: localhost:8848
      namespace: dev
      group: myGroup
      cluster: default
  service:
    vgroup-mapping:
      seata-server-group: default
3.2.2 项目中使用 Seata AT 模式

因为 Seata 的 AT 模式是无侵入的,我们只需要在开启分布式事务的方法上添加 @GlobalTransactional 注解即可。示例代码如下:

	@DeleteMapping("order")
    @GlobalTransactional(name = "business", rollbackFor = Exception.class)
    public void deleteOrder(@RequestParam("orderId") Long orderId) {
        customerService.deleteOrder(orderId);
    }

	@Override
    @Transactional
    public void deleteOrder(Long orderId) {
        stockService.recoverStock(orderId);
        orderService.deleteOrder(orderId);
        // 执行到此处显式抛出异常
        throw new RuntimeException("Branch transaction exception.");
    }

在以上演示代码的 deleteOrder 方法中,调用了 stockService 服务的 deleteOrder,此处是一个 openFeign 的远程调用。不属于本地的事务。如果调用该方法后,还发现订单未删除,说明分布式事务生效了,该全局事务下的所有分支事务都进行了回滚。

我在本地新下了一个订单,系统生成的订单ID 为 8.

在这里插入图片描述

然后调用上述代码中提供的订单删除接口。
在这里插入图片描述

再次查询订单ID 为 8 的订单,发现并该订单还在。
在这里插入图片描述
以上就是 seata 框架 AT 模式下的演示。Seata 的 AT模式对业务无侵入,不用对项目代码进行改造,只需要在对应位置处加上相应注解即可。改造工作量很小。

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

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

相关文章

判断闰年(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int year 2000;//执行循环判断&#xff1b;while (year < 2010){//执行流程&#xff1b;//判断能否整除4&#xff1…

十二、CyclicBarrier

CyclicBarrier 栅栏 什么时候满了开始发车 CyclicBarrier barrier new CyclicBarrier(20, () -> System.out.println("满人"));满20人开始发车每一个线程到达 barrier.await();都等着当 barrier.await();到达20人就开始往下执行 &#xff0c;发车 public…

struts2 CVE-2021-31805-struts2 s2-061 ONGL远程代码执行复现

复现&#xff1a; 切换到vulhub开启并且查看靶场的端口 访问页面 我们可以输入下面的命令来测试一下&#xff0c;执行6*6&#xff0c;查看源代码发现可以成功运行 接下来我们可以注入下面的payload来进行代码执行&#xff0c;执行id来查看当前用户 POST / HTTP/1.1 Host: x.x.…

探索Spring中的属性注入:@Value注解解析与应用

探索Spring中的属性注入&#xff1a;Value注解解析与应用 探索Spring中的属性注入&#xff1a;Value注解解析与应用摘要引言正文作用代码准备示例注入字符串注入属性注入Bean及其属性 其他属性注入优先级问题对Value属性注入的扩展Spring Boot对Value类型转换的扩展 代码案例演…

【GameFramework框架内置模块】8、文件系统(File System)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a; https://blog.csdn.net/q7…

代码随想录算法训练营第二十七天(第二十六天休息)|40.组合总和II

40.组合总和II 题目 给定一个数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。 说明&#xff1a; 所有数字&#xff08;包括目标数&#xff09;都是正整数。解集…

Android 开发环境搭建(Android Studio 安装图文详细教程)

Android Studio 下载 https://developer.android.google.cn/studio?hlzh-cn Android Studio 安装 检查电脑是否启用虚拟化 如果没有开启虚拟化&#xff0c;则需要进入电脑的 BIOS 中开启 直接 next选择安装的组件&#xff0c;Android Studio 和 Android 虚拟设备&#xff…

使用蜂鸟地图完成楼层自定义、房间着色、热力图、添加图片覆盖物、添加dom覆盖物、定位到固定区域的中心点

项目里有用到蜂鸟地图的地方&#xff0c;虽然有跟她们对接&#xff0c;但看他们文档也挺费劲的&#xff0c;要自己慢慢研究好久&#xff0c;有些实在研究不出来让他们帮忙看代码发现一些问题&#xff0c;所以把我发现的需要注意的一些点发上来&#xff0c;希望可以帮助到部分有…

深入探讨ChatGPT:技术突破与应用前景

目录 一、ChatGPT究竟是什么&#xff1f; 二、ChatGPT的发展脉络 三、ChatGPT的突出优势 强大的语言生成能力 多场景适应性 多语言处理能力 广泛的应用范围 数据敏感性的重视 四、结语&#xff1a;ChatGPT的未来与挑战 Tips&#xff1a;国内的ChatGPT ⭐ 点击进入Chat…

【GIS系列】GeoTools简介及工具类分享

本文将对GeoTools相关概念进行介绍&#xff0c;同时会给大家分享我工作中用到的工具类及使用方法。 作者&#xff1a;后端小肥肠 目录 1.前言 2. GeoTools简介 3. Geotools使用示例 3.1. 开发环境搭建 3.1.1. 所需版本和工具 3.1.2. pom依赖​​​​​​​ 4. 工具类介绍…

为什么不用 index 做 key 引出的 Diff 算法

文章目录 问题分析 问题 大家耳熟能详的最常见的问题就是 为什么不用 index 做 key &#xff0c;在刚开始学习的时候&#xff0c;只是知道在 v-for 做循环遍历的时候需要加上唯一标识 key&#xff0c;但好像都是这么写的 v-for"(item,index) in dictList" :key"…

IT系统可观测性

什么是可观测性 可观测性&#xff08;Observability&#xff09;是指能够从系统的外部输出推断出系统内部状态的能力。在IT和云计算领域&#xff0c;它涉及使用软件工具和实践来收集、关联和分析分布式应用程序以及运行这些应用程序的硬件和网络产生的性能数据流。这样做可以更…

数据分析-Pandas序列时间移动窗口化操作

数据分析-Pandas序列时间移动窗口化操作 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表…

【考研数学】全年保姆级规划+资料选择

直接跟考研课学即可 跟宋浩学第一遍说是基础&#xff0c;但宋浩的课程毕竟针对的是大学期末考试&#xff0c;基础知识方面讲的可能没有考研课程全面&#xff0c;毕竟大学课程的授课时间和考核要求不同于考研。 备考之前对自己做一个评估&#xff0c;每一个要准备的科目和其中…

代码随想录 动态规划-0-1背包问题

目录 标准0-1背包问题 二维dp数组01背包 一维dp数组01背包&#xff08;滚动数组&#xff09; 416.分割等和子集 1049.最后一块石头的重量|| 494.目标和 474.一和零 背包问题的分类 标准0-1背包问题 46. 携带研究材料&#xff08;第六期模拟笔试&#xff09; 时间限制…

超越传统的极限:解密B树与B+树的数据结构之美!

超越传统的极限&#xff1a;解密B树与B树的数据结构之美&#xff01; B树和B树是在计算机科学中常用的平衡查找树数据结构&#xff0c;它们在处理大规模数据和磁盘存储方面具有重要的优势。本文将深入介绍B树和B树的基本概念、特点以及它们在数据库和文件系统中的应用&#xff…

【德语常识】分类单词

【德语常识】分类单词 一&#xff0c;Colors二&#xff0c;Countries & Languages三&#xff0c; 一&#xff0c;Colors 二&#xff0c;Countries & Languages 三&#xff0c;

JNDI注入原理及利用IDEA漏洞复现

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

VMware虚拟机Centos7硬盘扩容详细图文教程

这里写自定义目录标题 设置扩容空间容量10G为列子开机后输入df -h 查看磁盘空间运行fdisk -l&#xff0c;查看硬盘信息运行fdisk /dev/sda输入m&#xff0c;查看n为add new partition&#xff0c;输入n输入p &#xff0c;p之后的东西都选择为默认再输入t&#xff0c;分区号根据…

洛谷-P1449 后缀表达式

目录 何为后缀表达式&#xff1f; 模拟过程 AC代码 采用STL的stack 题目链接&#xff1a;P1449 后缀表达式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 何为后缀表达式&#xff1f; 那后缀表达式是怎么算的呢 那显然就需要引用最开始说的栈了 因为后缀表表达式本来就是栈…