【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、dbmode = "db"## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource = "druid"## 配置的数据库类型dbType = "mysql"## 数据库连接驱动driverClassName = "com.mysql.jdbc.Driver"## 数据库连接 URLurl = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"## 数据库连接账号密码user = "fuyou"password = "fuyou3323"## 数据库连接配置minConn = 5maxConn = 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、sofatype = "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 = InnoDBDEFAULT 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 = InnoDBDEFAULT 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 = InnoDBDEFAULT 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 = InnoDBAUTO_INCREMENT = 1DEFAULT 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-groupseata:application-id: orderregistry:type: nacosnacos:application: seata-serverserver-addr: localhost:8848namespace: devgroup: myGroupcluster: defaultservice: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@Transactionalpublic 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.hqwc.cn/news/548901.html

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

相关文章

二叉搜索树题目:将有序链表转换为二叉搜索树

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;将有序链表转换为二叉搜索树 出处&#xff1a;109. 将有序链表转换为二叉搜索树 难度 5 级 题目描述 要求 …

士兵排列问题

解法一&#xff1a; deque实现队头入队和队尾入队即可得到编号排列&#xff0c;每个士兵有二个属性&#xff1a;编号、能力值。 #include<iostream> #include<algorithm> #include<deque> #include<vector> using namespace std; #define endl \n st…

苍穹外卖-day04:项目实战-套餐管理(新增套餐,分页查询套餐,删除套餐,修改套餐,起售停售套餐)业务类似于菜品模块

苍穹外卖-day04 课程内容 新增套餐套餐分页查询删除套餐修改套餐起售停售套餐 要求&#xff1a; 根据产品原型进行需求分析&#xff0c;分析出业务规则设计接口梳理表之间的关系&#xff08;分类表、菜品表、套餐表、口味表、套餐菜品关系表&#xff09;根据接口设计进行代…

搜索二叉树迭代和递归的两种*简单*实现方式

判断搜索二叉树 概念 一棵树所有结点的左节点小于父节点&#xff0c;右节点大于父节点&#xff0c;则为搜索二叉树。 迭代方法 中序遍历二叉树&#xff0c;如果总是升序则是搜索二叉树。如果存在降序&#xff0c;那肯定不是搜索二叉树。 Coding checkTreeOrder()方法 boo…

python 基础知识点(蓝桥杯python科目个人复习计划65)

今日复习内容&#xff1a;做题 例题1&#xff1a;遥远的雪国列车 问题描述&#xff1a; 小蓝和小红今天在房间里一起看完了“雪国列车”这部电影&#xff0c;看完之后他们感触颇深&#xff0c;同时他们想到了这样一道题目&#xff1a; 现在有一个数轴&#xff0c;长度为N&a…

代码随想录算法训练营第二十五天|216.组合总和III,17.电话号码的字母组合

216.组合总和III 题目 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数&#xff0c;并且每种组合中不存在重复的数字。 说明&#xff1a; 所有数字都是正整数。 解集不能包含重复的组合。 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 示例 2: 输入…

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…

想要了解更多商品信息?淘宝天猫详情接口API助你一键搞定!

想要了解更多商品信息&#xff1f;淘宝天猫详情接口API是你的理想选择&#xff01;作为唯一提供官方商品数据的接口&#xff0c;它能够帮助你快速获取商品的多种详细信息&#xff0c;联讯数据让你在购物过程中做出更明智的决策。 简介&#xff1a;淘宝天猫详情接口API的功能及…

【C语言_数组_复习篇】

目录 一、数组的概念 二、数组的类型 三、一维数组 3.1 一维数组的创建 3.2 一维数组的初始化 3.3 一维数组的访问 3.4 一维数组在内存中的存储 四、二维数组 4.1 二维数组的创建 4.2 二维数组的初始化 4.3 二维数组的访问 4.4 二维数组在内存中的存储 五、字符串数组 5.1…

干货分享,大厂内部压测方案设计!

01、为什么要做压测 1、什么是压力测试&#xff1f; 不断向被测对象施加压力&#xff0c;测试系统在压力情况下的表现。 2、压力测试的目的是什么&#xff1f; 测试得出系统的极限性能指标&#xff0c;从而给出合理的承诺值或者容量告警&#xff1b; 找出系统的性能瓶颈&a…

检查约束

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 检查约束 检查约束指的是在数据列上设置一些过滤条件&#xff0c;当过滤条件满足的时候才可以进行保存&#xff0c;如果不满足则出现错误。例如&#xff0c;设置年龄的信息…

低成本3D打印上位机,wifi增强器(棒子),刷Klipper

WIFI增强器(棒子)刷写Klipper&#xff0c;作为3D打印机的上位机。 本文中采用的棒子&#xff1a;骁龙410 512mb4g 1.下载安装必要软件 下载地址&#xff1a;https://pan.baidu.com/s/1QjYFknUYGnVMKYablFnZfQ?pwdukr9 提取码&#xff1a;ukr9 下载完成后总共四个文件 …