分布式事务一站式解决方案-Seata
分布式事务一站式解决方案-
- 分布式事务一站式解决方案
- 分布式事务产生背景
- 三个概念
- Seata下载和安装
- 实际业务模拟演示
- 不加 @GlobalTransactional 注解,正常操作下单
- 不加 @GlobalTransactional 注解,下单过程出异常或者超时了
- 加 @GlobalTransactional 注解,下单过程出异常或者超时了
- 原理
- undo_log 表作用
- 二阶段提交原理
- 分布式事务的执行流程(下订单-减库存-账户更新)
分布式事务一站式解决方案
分布式事务产生背景
一般来说,如果是微服务架构,会采用分布式系统开发,既然是多个微服务,那肯定是有多个独立的数据库的,那问题来了,
所以迫切希望提供一种分布式事务,解决微服务架构下的分布式事务问题
三个概念
Seata下载和安装
下载就不说了,直接去官方网站下载最新版本即可,注意安装 Seata 之前需要启动 Nacos,下载 Nacos 后直接 startup.cmd -m standalone
启动即可。
解压 seata-server-2.0.0.zip ,然后进入 conf 目录,更改 application.yml 配置如下,注意 console.user.username
console.user.password
seata.security.secretKey
seata.security.tokenValidityInMilliseconds
这4个一定要配置,不然启动报错
20:37:31.390 WARN --- [ main] [letWebServerApplicationContext] [ refresh] [] : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'userDetailsService';
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customUserDetailsServiceImpl': Injection of autowired dependencies failed;
nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'console.user.username' in value "${console.user.username}"
20:39:36.510 WARN --- [ main] [letWebServerApplicationContext] [ refresh] [] : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'tokenProvider';
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtTokenUtils': Injection of autowired dependencies failed;
nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'seata.security.secretKey' in value "${seata.security.secretKey}
server:port: 7091
spring:application:name: seata-server
logging:config: classpath:logback-spring.xmlfile:path: ${log.home:${user.home}/logs/seata}extend:logstash-appender:destination: 127.0.0.1:4560kafka-appender:bootstrap-servers: 127.0.0.1:9092topic: logback_to_logstash
console:user:username: seatapassword: seata seata:security:secretKey: 'seata'tokenValidityInMilliseconds: 1000000config:# support: nacos 、 consul 、 apollo 、 zk 、 etcd3type: nacosnacos:server-addr: 127.0.0.1:8848namespace:group: SEATA_GROUPusername: nacospassword: nacoscontext-path:##if use MSE Nacos with auth, mutex with username/password attribute#access-key:#secret-key:# data-id: seataServer.propertiesregistry:# support: nacos 、 eureka 、 redis 、 zk 、 consul 、 etcd3 、 sofatype: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPnamespace:cluster: defaultusername: nacospassword: nacoscontext-path:##if use MSE Nacos with auth, mutex with username/password attribute#access-key:#secret-key:store:# support: file 、 db 、 redis 、 raftmode: dbsession:mode: dblock:mode: dbfile:dir: sessionStoremax-branch-session-size: 16384max-global-session-size: 512file-write-buffer-cache-size: 16384session-reload-read-size: 100flush-disk-mode: asyncdb:datasource: druiddb-type: mysqldriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.133.128:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=GMTuser: rootpassword: rootmin-conn: 10max-conn: 100global-table: global_tablebranch-table: branch_tablelock-table: lock_tabledistributed-lock-table: distributed_lockquery-limit: 1000max-wait: 5000
启动成功便可以进入 Seata 前端管理页面
同时 Nacos 也可以看到 Seata 服务注册上来了
实际业务模拟演示
本次学习会用到如下三个模块,对应三个微服务,每个服务用的单独的数据库,其中 cloud-seata-order-service2001 是订单微服务、cloud-seata-storage-service2002 是库存微服务、cloud-seata-account-service2003 是账户微服务,具体的代码可以去 github 上获取
项目用的数据库及表如下
初始阶段,订单表、库存表、账户表数据如下:
不加 @GlobalTransactional 注解,正常操作下单
实际的业务需求就是 下订单-扣减库存-更新账户余额,由于现在是分布式系统,如何保证事务呢?
先看看正常操作下单,1号用户花费100块买了10个1号产品,发送如下请求 http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
, 此时数据库表数据是正常的
不加 @GlobalTransactional 注解,下单过程出异常或者超时了
假设,在下订单->扣减库存->更新账户余额,在更新账户余额这一步代码逻辑超时了或者出异常了,为什么这里超时设置的是65秒?因为 OpenFeign 远程调用的默认超时时间是 60秒
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {@Resourceprivate AccountMapper accountMapper;@Overridepublic void decrease(Long userId, Long money) {log.info("------------->AccountService 开始扣减余额");accountMapper.decrease(userId, money);log.info("------------->AccountService 开始扣减余额");// 超时异常timeout();// 抛出异常// int i = 10 / 0;}private void timeout() {try {TimeUnit.SECONDS.sleep(65);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
重启项目再发送一次请求,发现页面提示超时了,被全局异常捕获
查看表数据,发现库存扣减了,余额扣减了,但是订单状态是创建中…,这显然是有问题的
加 @GlobalTransactional 注解,下单过程出异常或者超时了
同样的过程,有了 GlobalTransactional 后,经过测试,正常下单,没问题;下单过程出异常或者超时了,数据库正确回滚,结果符合预期
@Override
// 微服务项目可能会有多个方法需要保证分布式事务,name 可以很好的区分
@GlobalTransactional(name = "create-order-transaction", rollbackFor = Exception.class)
public void create(Order order) {// xid全局事务检查String xid = RootContext.getXID();// 1. 新建订单log.info("-------------> 开始新建订单, XID: {}", xid);order.setStatus(0);int result = orderMapper.insertSelective(order);Order orderFromDB;if (result > 0) {orderFromDB = orderMapper.selectOne(order);log.info("-------------> 新建订单成功, OrderInfo: {}", orderFromDB);// 2. 扣减库存log.info("-------------> 开始扣减库存");storageFeignApi.decrease(orderFromDB.getProductId(), orderFromDB.getCount());log.info("-------------> 扣减库存成功");// 3. 扣减账户余额log.info("-------------> 开始扣减余额");accountFeignApi.decrease(order.getUserId(), order.getMoney());log.info("-------------> 扣余额存成功");// 4. 修改订单状态log.info("-------------> 开始修改订单状态");Example whereCondition = new Example(Order.class);Example.Criteria criteria = whereCondition.createCriteria();criteria.andEqualTo("id", orderFromDB.getId());criteria.andEqualTo("status", 0);orderFromDB.setStatus(1);int updateResult = orderMapper.updateByExampleSelective(orderFromDB, whereCondition);log.info("-------------> 修改订单状态成功");}log.info("-------------> 结束新建订单, XID: {}", xid);
}
原理
答案就是 二阶段提交,从日志输出也可以看到,PhaseTwo_Rollbacked->二阶段回滚了…
2024-08-07T21:59:43.441+08:00 INFO 26572 --- [seata-account-service] [nio-2003-exec-2] e.wong.service.impl.AccountServiceImpl : ------------->AccountService 开始扣减余额
2024-08-07T22:00:43.500+08:00 INFO 26572 --- [seata-account-service] [h_RMROLE_1_1_24] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:BranchRollbackRequest{xid='192.168.133.1:8091:2414480718769926145', branchId=2414480718769926148, branchType=AT, resourceId='jdbc:mysql://192.168.133.128:3306/seata_account', applicationData='null'}
2024-08-07T22:00:43.501+08:00 INFO 26572 --- [seata-account-service] [h_RMROLE_1_1_24] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.133.1:8091:2414480718769926145 2414480718769926148 jdbc:mysql://192.168.133.128:3306/seata_account
2024-08-07T22:00:43.542+08:00 INFO 26572 --- [seata-account-service] [h_RMROLE_1_1_24] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.133.1:8091:2414480718769926145 branch 2414480718769926148, undo_log deleted with GlobalFinished
2024-08-07T22:00:43.543+08:00 INFO 26572 --- [seata-account-service] [h_RMROLE_1_1_24] i.seata.rm.datasource.DataSourceManager : branch rollback success, xid:192.168.133.1:8091:2414480718769926145, branchId:2414480718769926148
2024-08-07T22:00:43.543+08:00 INFO 26572 --- [seata-account-service] [h_RMROLE_1_1_24] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
undo_log 表作用
而且上面你会发现每个微服务数据库都有张 undo_log 表,这个是做什么的呢?
在订单业务方法加了GlobalTransactional 注解后,打开 65 秒注释,目的是观察中间状态,重新请求 http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
,打开 undo_log 表,发现此时有数据
JSON 格式化后结构如下
当然此时在 Seata 管理页面也可以看到分布式事务中间状态的数据
不过 undo_log 表的数据在事务成功提交或者回滚之后在自动删除
二阶段提交原理
第一阶段
二阶段有分成两种情况:正常提交和异常回滚
分布式事务的执行流程(下订单-减库存-账户更新)
相关文章:

分布式事务一站式解决方案-Seata
分布式事务一站式解决方案- 分布式事务一站式解决方案分布式事务产生背景三个概念Seata下载和安装实际业务模拟演示不加 GlobalTransactional 注解,正常操作下单不加 GlobalTransactional 注解,下单过程出异常或者超时了加 GlobalTransactional 注解&…...
openwrt 使用ftace工具追踪协议栈转发流程
开这四个宏 CONFIG_KERNEL_DYNAMIC_FTRACEy CONFIG_KERNEL_FTRACEy CONFIG_KERNEL_FUNCTION_GRAPH_TRACERy CONFIG_KERNEL_FUNCTION_TRACERy 如果/sys/kernel/debug/tracing没有,可以挂载 mount -t debugfs nodev /sys/kernel/debug 挂载报错: mo…...

ElasticSearch优化实战:打造高性能搜索引擎的秘籍
在当今这个大数据时代,信息的海量增长对搜索技术提出了前所未有的挑战。用户不仅需要快速准确地从数以亿计的数据中找到所需信息,还希望搜索引擎能够提供个性化和智能化的搜索体验。ElasticSearch作为市场上领先的搜索引擎,因其强大的全文搜索…...

【STL】| C++ 栈和队列(详解、容器适配器的初步引入)
目录 前言 总代码 容器适配器的引入 栈 stack 队列 queue 栈和队列用法简介 栈 队列 deque简介(了解即可) 结语 前言 今天我们要讲解的结构是栈和队列 这两个的具体实现相比于前面我们学的string、vector、list都要简单得多(因为容…...

xss漏洞(二,xss靶场搭建以及简单利用)
本文仅作为学习参考使用,本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 一,环境搭建。 使用工具:PHP study,dvwa靶场。 1,GitHub上下载dvwa到PHP study的WWW文件夹内,并解压。 dvwa下载地址 …...

深度学习--------------Kaggle房价预测
目录 下载和缓存数据集访问和读取数据集总代码 数据预处理训练K折交叉验证模型选择总代码提交你的Kaggle预测提交Kaggle 下载和缓存数据集 import hashlib import os import tarfile import zipfile import requests# download传递的参数分别是数据集的名称、缓存文件夹的路径…...
cpio 命令
前言 cpio(Copy In and Out)是一种在类 Unix 操作系统中处理归档文件的多功能工具。与 tar 不同,cpio 有其独特的优势和使用场景,特别是在与其他命令结合使用时。本文将带你了解 cpio 的基础知识、用法及实际示例。 什么是 cpio…...
TreeMap自定义排序
我们都知道TreeMap可以根据key按字典升序排序。但在某些场景下,我们需要自定义排序规则,为了代码优雅一些,我们也希望在stream中groupingBy时自定义排序规则,就可以参考本文的实现。 1. 使用TreeMap默认的排序规则(按…...
我的CSDN 512天创作纪念日-20240807
机缘 在 2023 年 3 月 13 日,我撰写了第一篇技术博客《软考高级-系统分析师-案例分析-系统维护与设计模式》。那一天,我决定将自己的实战项目经验和学习心得记录下来,与更多志同道合的朋友分享。成为一名专业 IT 作者的梦想,促使我…...

微服务-实现nacos的集群和Gateway网关的实现、认证校验、解决跨域
1. nacos的集群模式 1.1 分析 nacos在企业中的使用100%都是集群模式。需要掌握nacos集群的搭建 nacos的数据存放在derby本地磁盘中,nacos集群模式会导致数据库数据不一致,使用加一层思想,修改nacos的数据库,使用mysql数据库&…...

数据库中的约束,聚合函数以及联合查询
目录 数据库中的约束 not null unique default primary key foreign key 表的设计 聚合函数(查询) 分组 联表查询(多表查询) 内连接 外连接 左外连接 右外连接 自连接 子查询 合并查询 数据库中的约束 为了保证…...

【AI大模型】Ollama+OpenWebUI+llama3本地大模型
本地部署大模型 0.引言1.部署安装1.1部署工具1.2 概念介绍1.3 ollama安装后的基本使用1.4 大模型权重下载1.4.1 ollama在线下载1.4.2 huggingFace下载大模型权重及如何使用ollama进行调用 2.带有UI界面的使用3.参考 0.引言 (1)目的 本教程主要关于开源A…...
习题20240807
文章目录 题目 1: 泛型类题目 2: 泛型方法题目 3: 泛型接口题目 4: 泛型约束题目 5: 泛型集合题目6:题目7:题目8:题目9: 题目 1: 泛型类 编写一个泛型类 Box,它能够存储一个类型为 T 的值,并提供方法 SetI…...

src挖掘-记一次有趣的逻辑漏洞分享
漏洞挖掘-记一次有趣的逻辑漏洞挖掘 前言简述1、信息收集2、找功能点3、挖掘漏洞案例一:任意用户注册漏洞案例二-垂直越权导致管理员密码重置获得管理员后台权限漏洞总结 前言 此文章是小白的学习笔记,请不要利用文章内相关知识点进行非法渗透ÿ…...

【C++】STL | list (链表)详解及重要函数的实现
目录 前言 总代码 ListNode类框架的建立 (成员与模板) list类的框架 普通构造 与 empty_init(适合不同类型构造函数的小函数) list的迭代器 引子 operator、operator--(前置与后置) operator 与 operator! operator* 与 …...

警惕智能手机的“隐形眼”:如何保护我们的数字隐私堡垒
随着智能手机深入我们生活的方方面面,它变得无所不在,甚至无所不知。 但你是否意识到,你的手机可能正在悄无声息地“监听”你的一举一动? 从你的搜索习惯到日常对话,手机的个性化推荐系统正不断收集你的数据。 本文…...
人工智能算法工程师(高级)课程12-自然语言处理之NLP的语言模型-ELMo,transformer,BERT与代码详解
大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(高级)课程12-自然语言处理之NLP的语言模型-ELMo,transformer,BERT与代码详解。本课程面向高级人工智能算法工程师,深入讲解自然语言处理(NLP)中的关键语言模型技术,包括了EMLo和transformer架构。此外,课程还详细…...

PicGo + gitee 免费搭建个人图床
目录 1 图床概念2 使用gitee和PicGo搭建图床流程2.1 下载安装PicGo工具 3 图片上传错误处理3.1 PicGo客户端提示404错误信息图片上传失败3.2 PicGo客户端提示400错误信息图片上传失败 1 图床概念 "图床"是一个网络术语,它指的是一种用于存储和托管图片…...
组合数的低复杂度运算
题源 题目 F. 预期中位数 每次测试的时间限制:3 秒 每次测试的内存限制:256 兆字节 Arul 有一个长度为 n 的二进制数组* a。 他将取该数组中所有长度为 k(k 为奇数)的子序列并找到它们的中位数。 所有这些值的总和是多少…...

小型并网式光伏气象站:光伏电站的智能守护者
小型并网式光伏气象站以其独特的功能和优势,成为了电站高效运行的智能守护者。小型并网式光伏气象站通过精准的数据采集与分析,为光伏电站的运维管理提供了强有力的支持。 小型并网式光伏气象站能够实时监测并记录光伏电站周围环境的多种气象参数&#x…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...