分布式事务Seata使用

我们要学习seata,首先需要具备如下技术储备:

  • 数据库事务的基本知识;
  • maven工具的使用;
  • 熟悉SpringCloudAlibaba技术栈;
  • 掌握SpringDataJPA简单使用;

一. Seata基本概念

1.seata是什么

Seata是阿里巴巴中间件团队发起了开源项目,其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题,后来更名为 Seata。

Seata的设计目标是对业务无侵入,因此从业务无侵入的2PC方案着手,在传统2PC的基础上演进。它把一个分布式事务理解成一个包含了若干分支事务的全局事务。

全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据库的本地事务。

2.seata基本架构

听到这里,是不是觉得很晦涩?那么威哥通过一幅图来帮助你们进一步理解seata的架构:

通过这幅图,我们看到了seata的三个重要的组件,分别是TC TM RM。那么他们到底是什么东西呢?

  • TC:Transaction Coordinator事务协调器,管理全局的分支事务的状态,用于全局性事务的提交和回滚。
  • TM:Transaction Manager 事务管理器,用于开启、提交或者回滚全局事务。
  • RM:Resource Manager资源管理器,用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接受TC的命令来提交或者回滚分支事务。

3.seata执行流程

搞清楚了这几个组件的含义之后,那么seata的整个执行流程我们就可以梳理清楚了:

  • A服务的TM向TC申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID。
  • 服务的RM向TC注册分支事务,并及其纳入XID对应全局事务的管辖。
  • A服务执行分支事务,向数据库做操作。
  • A服务开始远程调用B服务,此时XID会在微服务的调用链上传播。
  • B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖。
  • B服务执行分支事务,向数据库做操作。
  • 全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚。
  • TC协调其管辖之下的所有分支事务,决定是否回滚。

二. 案例环境搭建

我们搞清楚Seata的相关概念之后,现在威哥带领大家实现一个需求:通过订单微服务实现下订单的操作,然后通知库存微服务进行库存的扣减。

1. 前期准备

我们需要先准备订单和商品实体类。

//商品
@Entity(name = "shop_product")
@Data
public class Product {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer pid;//主键private String pname;//商品名称private Double pprice;//商品价格private Integer stock;//库存
}//订单
@Entity(name = "shop_order")
@Data
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long oid;//订单idprivate Integer uid;//用户idprivate String username;//用户名private Integer pid;//商品idprivate String pname;//商品名称private Double pprice;//商品单价private Integer number;//购买数量
}

我们还需要准备项目必备的pom依赖:

这是父工程的pom.xml文件:

<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

2. 搭建对应的微服务

现在我们分别搭建商品微服务和订单微服务

2.1 创建公共通用模块

我们创建shop-common模块,专门存放一些公共的实体类和工具类,便于其他模块进行共享。

2.1.1 在公共模块添加相关的依赖

<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

然后把之前准备的实体类都拷贝到这个shop-common中来。

2.2 搭建订单微服务模块

2.2.1 添加必要依赖

取名shop-order,在这个模块里面添加相关的依赖。

<dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.qf.common</groupId><artifactId>springcloudAlibaba-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.qf.feign</groupId><artifactId>springcloudAlibaba-order-product-feign</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
</dependencies>

2.2.2 编写controller

在这里,我们编写一个下单操作的服务接口。

@RestController
@RequestMapping("order")
@Slf4j
public class OrderController {@AutowiredOrderService5 orderService5;@RequestMapping("prod/{pid}")public Order order(@PathVariable("pid") Integer pid){return orderService5.createOrder(pid);}
}

2.2.3 编写service

public interface OrderService {Order createOrder(Integer pid);
}
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@AutowiredOrderFeign orderFeign;@AutowiredOrderDao orderDao;@Overridepublic Order createOrder(Integer pid) {//查询指定的商品信息Product product = orderFeign.findProductByPid(pid);log.info("查询到的商品信息是:{}", JSON.toJSONString(product));//执行下单的操作Order order = new Order();order.setUid(1003);order.setUsername("测试Seata案例");order.setPid(pid);order.setPname(product.getPname());order.setPprice(product.getPprice());//设置订单中的商品数量order.setNumber(1);orderDao.save(order);log.info("订单创建成功,订单信息是:{}",JSON.toJSONString(order));//执行扣减库存的操作orderFeign.reduceStock(pid,order.getNumber());return order;}
}

2.2.4 编写feign客户端

@FeignClient(name = "service-product")
public interface OrderFeign {@RequestMapping("product/{pid}")public Product findProductByPid(@PathVariable("pid") Integer pid);@RequestMapping("product/reduceStock")void reduceStock(@RequestParam("pid") Integer pid,@RequestParam("number") Integer number);
}

2.2.5 编写dao

public interface OrderDao extends JpaRepository<Order,Integer> {
}

2.3 搭建商品微服务模块

2.3.1 添加必要依赖

取名shop-product,在这个模块里面添加相关的依赖。

<dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.qf.common</groupId><artifactId>springcloudAlibaba-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
</dependencies>

2.3.2 编写controller

@RestController
@Slf4j
@RefreshScope //配置信息的即时刷新
public class ProductController {@AutowiredProductService productService;//根据id查询对应的商品信息@RequestMapping("product/{pid}")public Product findProductByPid(@PathVariable("pid") Integer pid){Product product = productService.findProductByPid(pid);//JSON.toJSONString  把指定数据转换成json串log.info("查询到的对应的商品是:" + JSON.toJSONString(product));return product;}//扣减库存@RequestMapping("product/reduceStock")public void reduceStock(@RequestParam("pid") Integer pid,@RequestParam("number") Integer number){productService.reduceStock(pid,number);}}

2.3.3 编写service

public interface ProductService {Product findProductByPid(Integer pid);void reduceStock(Integer pid, Integer number);
}
@Service
public class ProductServiceImpl implements ProductService {@AutowiredProductDao productDao;@Overridepublic Product findProductByPid(Integer pid) {Optional<Product> optional = productDao.findById(pid);return optional.get();}@Overridepublic void reduceStock(Integer pid, Integer number) {Product product = productDao.findById(pid).get();product.setStock(product.getStock() -  number);productDao.save(product);}
}

2.3.4 编写dao

public interface ProductDao extends JpaRepository<Product,Integer> {
}

现在我们启动测试,目前代码是没有什么问题的。但是如果我手动模拟异常。具体操作如下:

此时我们再去测试,这个时候就出现了问题了。我们发现订单能够下单成功,但是库存没有扣减。这样就出现了数据不一致的事务问题。那么我们可以使用seata来帮我们解决问题。

三. 配置使用seata

1. 下载seata

同学们可以在如下资源链接上进行下载:

下载地址:https://github.com/seata/seata/releases/v0.9.0/

2. 配置Seata

我们下载下来之后,会是一个压缩包。我们把这个压缩包打开之后进行相关配置。

2.1 修改registry.conf,指定seata使用nacos注册中心

registry {# 支持的注册中心有:file 、nacos 、eureka、redis、zk、consul、etcd3、sofa  我们使用自己的注册中心即可  所以删除其他注册中心相关的配置type = "nacos"nacos {serverAddr = "localhost"namespace = ""cluster = "default"}}config {# file、nacos 、apollo、zk、consul、etcd3type = "nacos"nacos {serverAddr = "localhost"namespace = ""}}

2.2 修改nacos-config.txt,指定我们的服务名称

2.3 初始化seata在nacos中的配置

我们需要把seata相关的配置信息在nacos配置中心进行注册。

# 初始化seata 的nacos配置 # 注意: 这里要保证nacos是已经正常运行的 
cd conf 
nacos-config.sh 127.0.0.1

执行成功后可以打开Naco的控制台,在配置列表中,可以看到初始化了很多Group为SEATA_GROUP的配置。

2.4 启动seata服务

切换到bin目录执行以下命令:

cd bin
seata-server.bat -p 9000 -m file

启动后在 Nacos 的服务列表下面可以看到一个名为 serverAddr的服务。如果入下图所示,小伙伴们,seata服务启动成功!!!!!

3. 使用seata进行事务控制

3.1 初始化一张数据表,用来seata进行日志记录

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.2 在微服务中添加seata相关的依赖

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>

3.3 配置DataSourceProxyConfig代理数据源

Seata 是通过代理数据源实现事务分支的,所以需要配置 io.seata.rm.datasource.DataSourceProxy 的 Bean,且是 @Primary默认的数据源,否则事务不会回滚,无法实现分布式事务.

在shop-product和shop-order里面都添加如下配置类:

@Configurationpublic class DataSourceProxyConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource")public DruidDataSource druidDataSource() {return new DruidDataSource();}@Primary@Beanpublic DataSourceProxy dataSource(DruidDataSource druidDataSource) {return new DataSourceProxy(druidDataSource);}}
  • 在shop-product和shop-order的resources目录添加registry.conf(直接将seata里面的配置复制过来即可)。
  • 在shop-product和shop-order的resources目录添加bootstrap.yml里面,然后添加配置。

3.4 在shop-order微服务中开启全局事务

4. 结果测试

我们发生请求:http://localhost:8091/order5/prod/1

此时查看数据库,我们发现事务问题得到了控制。就是当发生异常的时候,下单的记录被回滚了,而且库存也没有出现扣减。

到现在,我们的分布式事务就得到了控制。小伙伴们,你们有没有学会呢?

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

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

相关文章

基于大模型(LLM)相互协商的情感分析

Sentiment Analysis through LLM Negotiations https://arxiv.org/abs/2311.01876https://arxiv.org/abs/2311.01876 1.概述 在情感分析任务中,传统的单一大型语言模型(LLM)通常只通过单轮输出来做出决策。这种方法的主要缺点是无法完美应对需要深入推理的复杂语言现象,…

Vue.js【路由】

初识路由 提到路由&#xff08;Route&#xff09;&#xff0c;一般我们会联想到网络中常见的路由器&#xff08;Router&#xff09;&#xff0c;那么路由和路由器之间有什么关联呢&#xff1f;路由是指路由器从一个接口接收到数据&#xff0c;根据数据的目的地址将数据定向传送…

【how2j JQuery部分】课后题答案及相关笔记

练习题 <script src"jquery.min.js"></script><script>$(function(){$(tr:odd).css({"background-color":"#f8f8f8"});}); </script> <style> table{border-collapse:collapse;width:90%;} tr{border-bottom-sty…

力扣每日一题108:将有序数组转换为二叉搜索树

题目 简单 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也…

VS编辑器下使用MFC完成学生成绩统计分析系统

背景&#xff1a; &#xff08;一&#xff09;实验目的 通过该实验&#xff0c;使学生掌握windows程序设计的基本方法。掌握学生管理中学生成绩统计分析方面的基本知识&#xff0c;熟练应用数据库技术完成对学生成绩管理活动中各类信息的增加、修改、删除、查询等操作。通过处…

解锁AI的神秘力量:LangChain4j带你步入智能化实践之门

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 在数字化转型的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;正逐渐成为推动企业创新和增长的关键力量。然而&#xff0c;将AI技术融入到日常业务流程并非易事&#xff0c;它…

tr,cut,sort,uniq,seq命令的使用

1.tr tr命令是一个文本处理工具&#xff0c;跟正则表达式&#xff0c;字符串。有替换&#xff0c;压缩&#xff0c;删除的功能。 -s压缩 -d删除 命令1&#xff1a;tr a b。将出现的a替换成b。 输入---aaa---bdd--- 输出---bbb---bdd--- 命令&#xff1a;tr -d [字符1字符…

Xshell7、XFtp无法安装报错问题

报错信息&#xff1a; xshell产品无法安装,请验证来源是否存在,是否可以访问 解决方案&#xff1a; 打开本地路径&#xff1a;C:\Program Files (x86)\InstallShield Installation Information 将次文件删掉&#xff0c;重新点击xshell安装程序&#xff0c;重新安装即可

H5视频付费点播打赏影视系统程序全开源运营版

这是一款视频打赏源码&#xff0c;勿做非法用途&#xff0c;由用户亲测功能完善&#xff0c;源码仅用于学习使用&#xff0c;分享链接是用户云盘&#xff0c;具有时效性&#xff0c;感兴趣的可以去学习。 thinkphp开发&#xff0c;前后端分离设计&#xff0c;支持游客登陆、VIP…

【竞技宝】欧冠:多特淘汰大巴黎进决赛,姆巴佩迷失

多特蒙德在本赛季欧冠半决赛第二回合较量中,跟大巴黎队狭路相逢。赛前,大部分球迷和媒体都看好坐拥姆巴佩的大巴黎队,可以靠着主场作战的优势,逆转多特蒙德晋级欧冠决赛。大巴黎队主场作战确实创造出不少得分机会,只可惜球队运气有些差,射门都打在了多特蒙德横梁上。反观多特蒙…

浅谈消息队列和云存储

1970年代末&#xff0c;消息系统用于管理多主机的打印作业&#xff0c;这种削峰解耦的能力逐渐被标准化为“点对点模型”和稍复杂的“发布订阅模型”&#xff0c;实现了数据处理的分布式协同。随着时代的发展&#xff0c;Kafka&#xff0c;Amazon SQS&#xff0c;RocketMQ&…

国内免费AI聊天机器人(ChatGPT)推荐(下)含ChatGPT4.0版本

作者主页&#xff1a;点击&#xff01; 国内免费AI推荐专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月7日10点39分 现在&#xff0c;国内各种免费AI聊天机器人(ChatGPT)层出不穷&#xff0c;功能强大、玩法多样&#xff0c;可以满足你的不同需求。 今天&a…

H7-TOOL的双硬件串口同时运行Modbus主机和从机方法,方便大家Modbus测试验证(2024-05-06)

H7-TOOL的双硬件串口同时运行Modbus主机和从机方法&#xff0c;方便大家Modbus测试验证&#xff08;2024-05-06&#xff09; 使用这种方法&#xff0c;仅使用一个TOOL就可以方便同时运行Modbus主机和从机。 【Modbus专题视频】 可以用来熟悉Modbus协议 BSP视频教程第23期…

VS编辑器下使用MFC完成研究生管理系统设计

背景&#xff1a; &#xff08;一&#xff09;实验目的 通过该实验&#xff0c;使学生掌握windows程序设计的基本方法。掌握学籍管理的基本内容&#xff0c;熟练应用数据库技术和通用组件&#xff0c;实现研究生信息的增、删、改、查功能。通过处理过程对计算机软件系统工作原…

2024最新 Gradle 入门教程

&#x1f680; 2024最新 Gradle 入门教程 &#x1f31f; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍…

在Mars3d实现cesium的ImageryLayer自定义瓦片的层级与原点

需要自定义瓦片层级和原点&#xff0c;所以需要自己写第三方图层&#xff0c;但是之前写的很多方法&#xff0c;图层控制和显隐以及透明度&#xff0c;需要跟之前的交互一直&#xff0c;改动量太大的话不划算&#xff0c;所以直接看Mars3d的layer基类&#xff0c;把重写的image…

ETCD 简介

ETCD 简介 1-etcd介绍 etcd 是 CoreOS 团队于 2013 年 6 月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。具有以下特点&#xff1a; 简单&#xff1a;安装配置简单&#xff0c;而且提供了 HTTP API 进行交互&#xff0c;使用也很简单键…

数据丢失不慌张,手机数据恢复一键解决!

如今手机已经成为我们生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;手机都扮演着重要的角色。随着使用时间的增加&#xff0c;手机数据丢失的问题也时常发生。那么手机数据恢复有哪些方法呢&#xff1f;面对这种情况&#xff0c;先不要慌张&#xff0c;本文将…

神经元(科普)

Ⅰ 神经元简介 神经元即神经元细胞&#xff0c;是神经系统最基本的结构和功能单位。 分为细胞体和突起两部分。 细胞体由细胞核、细胞膜、细胞质组成&#xff0c;具有联络和整合输入信息并传出信息的作用。 突起有树突和轴突两种。树突短而分枝多&#xff0c;直接由细胞体…

雪深监测站解析-云境天合

TH-XS2雪深监测站是一种用于监测雪深的设备&#xff0c;它们分布在全国各地的关键区域&#xff0c;如高速公路、铁路沿线、机场等重要交通节点&#xff0c;以及山区、林区等易发生雪灾的地区。这些监测站就像是气象工作者的“眼睛”&#xff0c;时刻关注着雪情的变化&#xff0…