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

目录

    • 一、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.kler.cn/a/273142.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

恒创科技:什么是BGP线路服务器?BGP机房的优点是什么?

在当今的互联网架构中&#xff0c;BGP(边界网关协议)线路服务器和BGP机房扮演着至关重要的角色。BGP作为一种用于在自治系统(AS)之间交换路由信息的路径向量协议&#xff0c;它确保了互联网上的数据能够高效、准确地从一个地方传输到另一个地方。那么&#xff0c;究竟什么是BGP…

vue中判断是否使用自定义插槽

在封装自定义组件时&#xff0c;需要判断使用者是否使用了插槽<slot"aaa">&#xff0c;如果没有则使用一个组件中默认的值&#xff0c;反之就用传入的内容<template name"aaa"></template>,实现如下&#xff1a; <div class"lin…

视频私有云,HDMI/AV多硬件设备终端接入,SFU/MCU视频会议交互方案。

在视频业务深入的过程中越来越多的硬件设备接入视频交互的视频会议中远程交互&#xff0c;有的是视频采集&#xff0c;有的是医疗影像等资料&#xff0c;都需要在终端承显&#xff0c;这就需要我们的设备终端能多设备&#xff0c;多协议接入&#xff0c;设备接入如下。 1&#…

【NTN 卫星通信】 TN和多NTN配合的应用场景

1 场景描述 此场景描述了农村环境&#xff0c;其中MNO (运营商TerrA)仅在城市附近提供本地地面覆盖&#xff0c;而MNO (SatA)提供广泛的NTN覆盖。SatA使用GSO轨道和NGSO轨道上的卫星。SatA与TerrA有漫游协议&#xff0c;允许:   所有TerrA用户的连接&#xff0c;当这些用户不…

Android FrameWork 学习路线

目录 前言 学习路线&#xff1a; 1.基础知识 2、AOSP 源码学习 3. AOSP 源码编译系统 4. Hal与硬件服务 5.基础组件 6. Binder 7. 系统启动过程分析 8. 应用层框架​编辑 9. 显示系统 10. Android 输入系统 11. 系统应用 前言 Android Framework 涉及的行业相当广…

行尾检测论文汇总

文章目录 2023GNSS-Free End-of-Row Detection and Headland Maneuvering for Orchard Navigation Using a Depth Camera 2023 GNSS-Free End-of-Row Detection and Headland Maneuvering for Orchard Navigation Using a Depth Camera 摘要&#xff1a; 果园中基于GPS的导航…

挖到宝了!这些内容管理平台是企业的最佳选择

内容管理系统&#xff0c;不再只是专业人士的语言&#xff0c;而是已经突破到普通人的视野中。简单易懂的解释就是&#xff0c;内容管理平台就像是一个大货仓&#xff0c;你可以在这里存储、整理和搜索你的所有资料。那么今天&#xff0c;我要向你推荐的是三款强大的内容管理平…

Kotlin进阶之协程从上车到起飞

公众号「稀有猿诉」 原文链接 Kotlin进阶之协程从上车到起飞 通过前面的一篇文章我们理解了协程的基本概念&#xff0c;学会协程的基本使用方法&#xff0c;算是正式入门了&#xff0c;接下来就是要深入的学习技术细节和高级使用方法&#xff0c;以期完全掌握Kotlin协程…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展应用教程

原文链接&#xff1a;“SRP模型”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展应用教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247597452&idx5&snf723d9e5858a269d00e15dbe2c7d3dc0&chksmfa823c6…

SSA优化最近邻分类预测(matlab代码)

SSA-最近邻分类预测matlab代码 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新型的群智能优化算法&#xff0c;在2020年提出&#xff0c;主要是受麻雀的觅食行为和反捕食行为的启发。 数据为Excel分类数据集数据。 数据集划分为训练集、验证集、测试集,比例为8&#…

代码随想录刷题day27|组合总和II组合总和II分割回文串

文章目录 day27学习内容一、组合总和-所选数字可重复1.1、代码-正确写法1.1.1、为什么递归取的是i而不是i1呢&#xff1f; 二、组合总和II-所选数字不可重复2.1、和39题有什么不同2.2、思路2.2.1、初始化2.2.2、主要步骤2.2.3、回溯函数 backTracking 2.3、正确写法12.3.1、为什…

四.排序(冒泡/选择)

目录 11-排序介绍 常见排序算法: 12-冒泡排序介绍 代码要求: 思路: 13-冒泡排序 代码: 14-选择排序 简单写法: 好的写法: 11-排序介绍 排序&#xff1a;将一组“无序”的记录序列调整为“有序”的记录序列。 列表排序&#xff1a;将无序列表变为有序列表 输入&#…

【数据结构与算法】:非递归实现快速排序、归并排序

&#x1f525;个人主页&#xff1a; Quitecoder &#x1f525;专栏&#xff1a;数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序&#xff0c;本篇我们来探究非递归实现快速排序和归并排序 目录 1.非递归实现快速排序1.1 提取单趟排序1.2 用栈实现的具体思路1.3 代码…

java方法的引用传递和值传递

1、方法的值参数传递 下面代码&#xff0c;它会在控制台输出什么&#xff1f; public class ArrayTest {public static void main(String[] args) {int number 100;System.out.println(number);change(number);System.out.println(number);}public static void change(int n…

【力扣精选算法100道】——带你了解(数组模拟栈)算法

目录 &#x1f4bb;比较含退格的字符串 &#x1f388;了解题意 &#x1f388;分析题意 &#x1f6a9;栈 &#x1f6a9;数组模拟栈 &#x1f388;实现代码 844. 比较含退格的字符串 - 力扣&#xff08;LeetCode&#xff09; &#x1f4bb;比较含退格的字符串 &#x1f3…

Python--类中作用域

1、在面向对象编程中&#xff0c;主要的变量就是成员变量&#xff08;属性&#xff09;和局部变量 class Cat:# 属性name Noneage None# n1, n2, result为局部变量def cal(self, n1, n2):result n1 n2print(f"result{result}") 2、作用域的分类&#xff1a;属性…

【精品】递归查询数据库 获取树形结构数据 通用方法

数据库表结构 实体类基类 Getter Setter ToString public class RecursionBean {/*** 编号*/private Long id;/*** 父权限ID&#xff0c;根节点的父权限为空*/JsonIgnoreprivate Long pid;private List<? extends RecursionBean> children;/*** 递归查询子节点** param…

PSCA复位控制集成之复位信号

组件可能支持两种基本的复位类型。 • 冷复位&#xff1a;重置组件中的所有逻辑。用作上电复位。 • 热复位&#xff1a;重置组件中的大部分逻辑。通常&#xff0c;复位的范围是所有功能逻辑。不包括在热复位中的逻辑会随组件类型而变化&#xff0c;但通常会排除诸如调试和 R…

STM32使用常见错误合集(正在更新版)

本文章记录一些学习STM32的一些错误问题 一、编译、烧录类问题 1、烧录不成功&#xff0c;Keil提示RDDI-DAP Error【场景&#xff1a;PWM驱动直流电机】 解决方案&#xff1a;将电机断开再进行烧录&#xff0c;断开后就可以美美烧录不报错啦~ 二、Keil使用问题 1、打开一个…

sqllab第二十六A关通关笔记

知识点&#xff1a; 布尔注入 只能爆破出不带空格的语句信息database() version() 等空格、注释都被过滤了错误不回显了 感觉和26关应该差不多 构造payload:id0||11 发现可以绕过 尝试进行错误注入 构造payload:id||exp(710)1 发现页面没有有价值的回显信息&#xff1b;…
最新文章