【Spring Cloud Alibaba】seata分布式事务官方入门案例(实战版)

文章目录

  • 1. 业务介绍
    • 1.1. 用例
    • 1.2. 架构图
    • 1.3. 3个服务的代码及业务逻辑(略)
  • 2. SEATA 的分布式交易解决方案
  • 3. 由Dubbo + SEATA提供支持的示例(实战)
    • 3.1. 步骤 1:建立数据库,如seata数据库
    • 3.2. 步骤 2:创建 UNDO_LOG 表
    • 3.3. 步骤 3:为示例业务创建表
    • 3.4. 步骤 4: 启动服务
    • 3.5. 步骤 5: 运行示例
    • 3.6. 测试
      • 3.6.1. 正常执行
      • 3.6.2. 模拟异常发生
      • 3.6.3. 原理简述

本文是介绍官方的seata入门案例,采用的是 微服务架构,参考地址:https://seata.io/zh-cn/docs/user/quickstart/。

代码详细地址:https://github.com/seata/seata-samples/tree/master/dubbo。

整个seata-samples代码库很大,本例只用到了dubbo子模块。

1. 业务介绍

1.1. 用例

本业务以购买商品的业务逻辑为例子。整个业务涉及到3个微服务一起提供服务:

  • 仓储服务:对给定的商品扣除仓储数量。
  • 订单服务:根据采购需求创建订单。
  • 帐户服务:从用户帐户中扣除余额。

1.2. 架构图

Architecture

1.3. 3个服务的代码及业务逻辑(略)

2. SEATA 的分布式交易解决方案

img

我们只需要使用一个 @GlobalTransactional 注解在业务方法上:

@GlobalTransactionalpublic void purchase(String userId, String commodityCode, int orderCount) {......}

备注:

1、每个微服务都与seata服务连接起来(可以看每个服务的xxx-service.xml)。如dubbo-storage-service.xml文件。其他服务也一样

    <bean id="storageDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy"><constructor-arg ref="storageDataSource" /></bean><bean name="storageDataSource" class="com.alibaba.druid.pool.DruidDataSource"init-method="init" destroy-method="close"><property name="url" value="${jdbc.storage.url}"/><property name="username" value="${jdbc.storage.username}"/><property name="password" value="${jdbc.storage.password}"/><property name="driverClassName" value="${jdbc.storage.driver}"/></bean>

2、每个服务都注册了一个GlobalTransactionScanner,字符串dubbo-demo-storage-service表示应用ID(applicationId),字符串my_test_tx_group表示事务服务组(txServiceGroup)

    <bean class="io.seata.spring.annotation.GlobalTransactionScanner"><constructor-arg value="dubbo-demo-storage-service"/><constructor-arg value="my_test_tx_group"/></bean>

3、字符串my_test_tx_group还在项目的file.conf文件中还用到了。

3. 由Dubbo + SEATA提供支持的示例(实战)

建议参考代码https://github.com/seata/seata-samples/tree/master/dubbo的readme.md文件。

3.1. 步骤 1:建立数据库,如seata数据库

3.2. 步骤 2:创建 UNDO_LOG 表

undo_log表是临时中间表,存储的是分布式事务过程中每张表变更前后的值。一条记录代表某张表变更前和变更后的详细数据。这张表的作用是需要读者理解的。

之所以记录变更前后的详细数据,就是为了在事务失败时能够进行回滚。

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

3.3. 步骤 3:为示例业务创建表

3.4. 步骤 4: 启动服务

# 启动seata服务
docker run --name seata-file -p 8091:8091 hellowoodes/seata:0.9.0-file
# 启动zooekper
docker run --name zookeeper -p 2181:2181 -p 2888:2888 -p 3888:3888 -d zookeeper

3.5. 步骤 5: 运行示例

注意:作者下载代码测试的时候,以下应用都不能启动,后查阅资料,发现是缺少了jar包,请在启动出问题的时候在pom.xml中添加jar包。

<dependency><groupId>com.alibaba.spring</groupId><artifactId>spring-context-support</artifactId><version>1.0.11</version>
</dependency>
  • 启动 DubboAccountServiceStarter
  • 启动 DubboStorageServiceStarter
  • 启动 DubboOrderServiceStarter
  • 运行 DubboBusinessTester for demo test

3.6. 测试

一共设计到3张表,初始状态下,库存storage_tbl存储了100个库存,订单表order_tbl为空,账户表account_tbl有999元。测试类模拟了用户购买了2件200元的商品。

3.6.1. 正常执行

如果一切正常,那么:

1、account_tbl表账户余额为999 - 200*2 = 599元

2、order_tbl表会生成一个订单

3、storage_tbl表的库存为100 - 2 = 98

测试结果如下图:

image-20231023010711440

3.6.2. 模拟异常发生

在业务方法上加了@GlobalTransactional注解,如果分布式事务生效,那么在发生异常Exception时,3张表的数据都会回滚,值还是初始值,即:

1、account_tbl表为999元

2、order_tbl是空的

3、storage_tbl是100件

添加模拟发生异常的代码:

image-20231023011144154

查看测试结果如下图:

观察到控制台发生了一场,且数据库中3张表的数据没有变动。

image-20231023011713581

3.6.3. 原理简述

全部3个微服务全部debug启动,且基于架构图,Account服务是最后调用的,在该服务上打上一个断点。然后去观察undo_log表中的内容。注意超时时间哦。

1、打上断点

在io.seata.samples.dubbo.service.impl.OrderServiceImpl#create的最后位置打上断点

2、观察undo_log表

image-20231023012240702

备注:刚好3条记录,对应3张表变更前后的内容;blob的内容查看需要再navicat中设置一下;然后复制格式化成json。

3、观察3张表的数据

下面是账户余额表account_tbl变更前后的数据情况(json格式化后):

{"@class": "io.seata.rm.datasource.undo.BranchUndoLog","xid": "172.17.0.4:8091:2151716199","branchId": 2151716201,"sqlUndoLogs": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.undo.SQLUndoLog","sqlType": "UPDATE","tableName": "account_tbl","beforeImage": {"@class": "io.seata.rm.datasource.sql.struct.TableRecords","tableName": "account_tbl","rows": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 2},{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "money","keyType": "NULL","type": 4,"value": 999}]]}]]},"afterImage": {"@class": "io.seata.rm.datasource.sql.struct.TableRecords","tableName": "account_tbl","rows": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Row","fields": ["java.util.ArrayList",[{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "id","keyType": "PRIMARY_KEY","type": 4,"value": 2},{"@class": "io.seata.rm.datasource.sql.struct.Field","name": "money","keyType": "NULL","type": 4,"value": 599}]]}]]}}]]
}

下面是订单表order_tbl:

{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.17.0.4:8091:2151716242","branchId":2151716245,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"order_tbl","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"order_tbl","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"order_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":6},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"user_id","keyType":"NULL","type":12,"value":"U100001"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"commodity_code","keyType":"NULL","type":12,"value":"C00321"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"money","keyType":"NULL","type":4,"value":400}]]}]]}}]]}

下面是库存表storage_tbl:

{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.17.0.4:8091:2151716242","branchId":2151716243,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"UPDATE","tableName":"storage_tbl","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":100}]]}]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":98}]]}]]}}]]}

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

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

相关文章

百分点科技再度亮相GITEX全球大会

10月16-20日&#xff0c;全球最大科技信息展会之一 GITEX Global 2023在迪拜世贸中心开展&#xff0c;本届展会是历年来最大的一届&#xff0c;吸引了来自180个国家的6,000家参展商和180,000名技术高管参会。 百分点科技作为华为生态合作伙伴&#xff0c;继去年之后再度参展&a…

【Godot】【BUG】4.x NavigationAgent 导航不生效

4.2.beta2 试了半天才发现原来默认只对第一个有导航的 TileMap 的第 1 层 生效&#xff0c;而我设置的导航层不是第一层&#xff0c;然后我新建了一个 TileMap 将导航的瓦片设置到这个 TileMap 上了&#xff0c;如图 这样就解决了问题&#xff0c;不用再修改默认设置的东西了&a…

【网络编程】基于epoll的ET模式下的Reactor

需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、Reactor介绍 二、基于epoll的ET模式下的Reactor计算器代码 1、Tcp…

[架构之路-241]:目标系统 - 纵向分层 - 企业信息化与企业信息系统(多台企业应用单机组成的企业信息网络)

目录 前言&#xff1a; 一、什么是信息系统&#xff1a;计算机软件硬件系统 1.1 什么是信息 1.2 什么是信息系统 1.3 什么是信息技术 1.4 什么是信息化与信息化转型 1.5 什么是数字化与数字化转型&#xff08;信息化的前提&#xff09; 1.6 数字化与信息化的比较 1.7 …

01、Python 安装 ,Pycharm 安装

目录 安装安装 Python安装 Pycharm 创建项目简单添加文件运行 简单爬取下载小视频 安装 python-3.8.10-amd64.exe – 先安装这个 pycharm-community-2022.2.exe 再安装这个 安装 Python python-3.8.10-amd64.exe 安装&#xff08;这个是其他版本的安装&#xff0c;步骤一样…

第五届太原理工大学程序设计竞赛新生赛(初赛)题解

第五届太原理工大学程序设计竞赛新生赛&#xff08;初赛&#xff09;题解 时隔半年重做一次&#xff0c;还是有几道不会&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; ⭐️A.饿饿饭饭 题目&#xff1a; &#x1f31f;题解&#xff1a; 很简单&#xff0c;签…

自然语言处理---Transformer机制详解之GPT2模型介绍

1 GPT2的架构 从模型架构上看, GPT2并没有特别新颖的架构, 它和只带有解码器模块的Transformer很像. 所谓语言模型, 作用就是根据已有句子的一部分, 来预测下一个单词会是什么. 现实应用中大家最熟悉的一个语言模型应用, 就是智能手机上的输入法, 它可以根据当前输入的内容智…

我做不到受每个人喜欢

我做不到受每个人喜欢 我想描述一下昨天发生争吵后我个人的观点&#xff0c;希望能够重新呈现出一种积极的态度。 首先&#xff0c;让我简要梳理一下事件的经过&#xff0c;当天我像往常一样去另一个宿舍找人聊天&#xff0c;可能因为说话声音有点大&#xff0c;坐在我后面的那…

Element Plus el-select选择框失去焦点blur

正常情况下&#xff0c;可以使用 el-select 自带的方法 blur 事件来使select失去焦点 示例&#xff1a; <el-select v-model"value" ref"selectRef"><el-optionv-for"item in options":key"item.value":label"item.la…

【MySQL系列】- Select查询SQL执行过程详解

【MySQL系列】- Select查询SQL执行过程详解 文章目录 【MySQL系列】- Select查询SQL执行过程详解一、SQL查询语句的执行过程二、SQL执行过程详解2.1. 连接器2.2. 查询缓存2.3. 分析器2.4. 优化器2.5. 执行器 三、undo log 和 redo log作⽤3.1. redo log &#xff08;重做日志&a…

软件工程与计算总结(十九)软件测试

目录 ​编辑 一.引言 1.验证与确认 2.目标 3.测试用例 4.桩与驱动 5.缺陷、错误与失败 二.测试层次 1.测试层次的划分 2.单元测试 3.集成测试 4.系统测试 三.测试技术 1.测试用例的选择 2.随机测试 3.基于规格的技术&#xff08;黑盒测试&#xff09; 4.基于代…

【数据结构】面试OJ题——时间复杂度

目录 一&#xff1a;旋转数组 思路&#xff1a; 二&#xff1a;消失的数字 思路&#xff1a; 一&#xff1a;旋转数组 189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负…