高度细化的SAGA模式实现:基于Spring Boot与RabbitMQ的跨服务事务
场景与技术栈
场景:电商系统中的订单创建流程,涉及订单服务(Order Service)、库存服务(Inventory Service)、支付服务(Payment Service)。
技术栈:
-
Java 11
-
Spring Boot 2.7.0
-
MySQL 8.0
-
RabbitMQ
项目结构与组件
-
order-service -
inventory-service -
payment-service -
saga-coordinator
代码实现
1. Saga Coordinator
@SpringBootApplicationpublic class SagaCoordinatorApplication { public static void main(String[] args) { SpringApplication.run(SagaCoordinatorApplication.class, args); }}@Service@RequiredArgsConstructorpublic class SagaService { private final SagaOrderService sagaOrderService; private final SagaInventoryService sagaInventoryService; private final SagaPaymentService sagaPaymentService; private final SagaTransactionRepository sagaTransactionRepository; public void placeOrder(Long orderId, Long productId, int quantity, double amount) throws ExecutionException, InterruptedException { SagaTransaction transaction = new SagaTransaction(); transaction.setOrderId(orderId); transaction.setProductId(productId); transaction.setQuantity(quantity); transaction.setAmount(amount); transaction.setStatus(SagaTransaction.Status.INITIATED); sagaTransactionRepository.save(transaction); CompletableFuture.runAsync(() -> sagaOrderService.createOrder(transaction)) .thenRunAsync(() -> sagaInventoryService.decreaseInventory(transaction)) .thenRunAsync(() -> sagaPaymentService.charge(transaction)) .exceptionally(ex -> { rollback(transaction); throw new RuntimeException("Saga failed", ex); }); } private void rollback(SagaTransaction transaction) { CompletableFuture.runAsync(() -> sagaPaymentService.refund(transaction)) .thenRunAsync(() -> sagaInventoryService.increaseInventory(transaction)) .thenRunAsync(() -> sagaOrderService.cancelOrder(transaction)) .exceptionally(ex -> { transaction.setStatus(SagaTransaction.Status.FAILED); sagaTransactionRepository.save(transaction); throw new RuntimeException("Rollback failed", ex); }); }}
2. SagaOrderService
@Servicepublic class SagaOrderService { private final OrderService orderService; private final RabbitTemplate rabbitTemplate; @Autowired public SagaOrderService(OrderService orderService, RabbitTemplate rabbitTemplate) { this.orderService = orderService; this.rabbitTemplate = rabbitTemplate; } public void createOrder(SagaTransaction transaction) { // 创建订单逻辑 // ... // 发送创建订单完成的消息 rabbitTemplate.convertAndSend("order.create", transaction); } public void cancelOrder(SagaTransaction transaction) { // 取消订单逻辑 // ... // 发送取消订单完成的消息 rabbitTemplate.convertAndSend("order.cancel", transaction); }}
3. SagaInventoryService
@Servicepublic class SagaInventoryService { private final InventoryService inventoryService; private final RabbitTemplate rabbitTemplate; @Autowired public SagaInventoryService(InventoryService inventoryService, RabbitTemplate rabbitTemplate) { this.inventoryService = inventoryService; this.rabbitTemplate = rabbitTemplate; } public void decreaseInventory(SagaTransaction transaction) { // 扣减库存逻辑 // ... // 发送扣减库存完成的消息 rabbitTemplate.convertAndSend("inventory.decrease", transaction); } public void increaseInventory(SagaTransaction transaction) { // 增加库存逻辑 // ... // 发送增加库存完成的消息 rabbitTemplate.convertAndSend("inventory.increase", transaction); }}
4. SagaPaymentService
@Servicepublic class SagaPaymentService { private final PaymentService paymentService; private final RabbitTemplate rabbitTemplate; @Autowired public SagaPaymentService(PaymentService paymentService, RabbitTemplate rabbitTemplate) { this.paymentService = paymentService; this.rabbitTemplate = rabbitTemplate; } public void charge(SagaTransaction transaction) { // 收款逻辑 // ... // 发送收款完成的消息 rabbitTemplate.convertAndSend("payment.charge", transaction); } public void refund(SagaTransaction transaction) { // 退款逻辑 // ... // 发送退款完成的消息 rabbitTemplate.convertAndSend("payment.refund", transaction); }}
SagaTransaction Entity
@Entitypublic class SagaTransaction { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long orderId; private Long productId; private int quantity; private double amount; private Status status; // INITIATED, COMPLETED, ROLLING_BACK, ROLLED_BACK, FAILED // Getters & Setters}
消息队列监听
在每个微服务中,需要添加消息队列的监听器,以便在接收到消息时执行相应的操作。例如,在order-service中:
@Componentpublic class OrderSagaListener { private final SagaOrderService sagaOrderService; @Autowired public OrderSagaListener(SagaOrderService sagaOrderService) { this.sagaOrderService = sagaOrderService; } @RabbitListener(queues = "order.create") public void handleOrderCreate(SagaTransaction transaction) { sagaOrderService.createOrder(transaction); } @RabbitListener(queues = "order.cancel") public void handleOrderCancel(SagaTransaction transaction) { sagaOrderService.cancelOrder(transaction); }}
额外细节
-
为确保事务的一致性,可以使用RabbitMQ的发布确认(Publisher Confirms)机制。
-
每个微服务的数据库事务应该使用
@Transactional注解来保证ACID属性。 -
需要设计失败重试和事务状态检查机制,确保在故障恢复时能够正确地执行补偿操作。
通过上述设计,SAGA模式与RabbitMQ的结合,不仅能够处理跨服务的事务,还能够通过消息队列实现服务解耦和消息的异步处理,提高系统的稳定性和可扩展性。
相关文章:
高度细化的SAGA模式实现:基于Spring Boot与RabbitMQ的跨服务事务
场景与技术栈 场景:电商系统中的订单创建流程,涉及订单服务(Order Service)、库存服务(Inventory Service)、支付服务(Payment Service)。 技术栈: Java 11 Spring Bo…...
Vue工程化开发
Vue工程化开发 一、工程化开发和脚手架 1.开发Vue的两种方式 核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。工程化开发模式:基于构建工具(例如:webpack)的环境中开…...
Ray_Tracing_The_Next_Week下
5image Texture Mapping 图像纹理映射 我们之前虽然在交点信息新增了uv属性,但其实并没有使用,而是通过p交点笛卡尔坐标确定瓷砖纹理或者大理石噪声纹理的值 现在通过uv坐标读取图片,通过std_image库stbi_load(path)…...
ES索引生命周期管理
基于如何 定时删除ES索引过期数据 而引发的一系列关于ES索引生命周期管理ILM(Index Lifecycle Management)的学习 快速上手 :定时删除ES索引中的过期数据 1. ILM解决什么问题? ES从6.7版本引入ILM,通过ILM可以解决哪些问题呢? 自动新建…...
Oracle数据库体系结构基础
关于Oracle体系结构 基于Oracle11g体系结构 目标: 了解Oracle体系结构掌握逻辑存储结构掌握物理存储结构熟悉Oracle服务器结构熟悉常用的数据字典 Oracle数据库管理中的重要的三个概念 实例(instance):实例是指一组Oracle后台进程以及在服务器中分配…...
QT学习笔记4.5(文件、参数文件)
QT学习笔记4.5(文件、参数文件) 1.保存配置参数 1.使用QSettings保存到注册表,ini文件 2.文件存储:使用 QFile 和其他类将参数保存到文本文件、二进制文件、XMLWENJIAN、JSON 文件等。 文本文件:以简单的键值对格式…...
服务器虚拟化的详细学习要点
服务器虚拟化的详细学习要点可以归纳为以下几个方面: 1. 基本概念与原理 定义与原理:了解服务器虚拟化是一种将物理服务器资源转化为虚拟服务器资源的技术,允许在一台物理服务器上运行多个虚拟服务器。 虚拟化层次:理解虚拟化的不同层次,如裸机虚拟化(Type 1)和托管虚…...
创建一个Java Web API项目
创建一个Java Web API涉及多个步骤和技术栈,包括项目设置、依赖管理、数据访问层实现、业务逻辑实现、控制层开发以及测试和部署。在这篇详解中,我将带领你通过一个完整的Java Web API实现流程,采用Spring Boot和MyBatis-Plus作为主要技术工具…...
对称加密算法的使用Java和C#
1. JAVA中的使用 1.1.原生使用 Main函数代码 import symmetric_encryption.AESExample; import symmetric_encryption.BlowfishExample; import symmetric_encryption.DESExample; import symmetric_encryption.TripleDESExample; public class App { public static…...
10款好用的开源 HarmonyOS 工具库
大家好,我是 V 哥,今天给大家分享10款好用的 HarmonyOS的工具库,在开发鸿蒙应用时可以用下,好用的工具可以简化代码,让你写出优雅的应用来。废话不多说,马上开整。 1. efTool efTool是一个功能丰富且易用…...
ubuntu22.04中备份Iptables的设置
在 Ubuntu 22.04 中备份 iptables 的设置,您可以采用以下几种方法: 使用 iptables-save 命令: 您可以使用 iptables-save 命令将当前的 iptables 规则保存到文件中。例如,要将规则保存到 /etc/iptables/rules.v4 文件中࿰…...
(PyTorch) 深度学习框架-介绍篇
前言 在当今科技飞速发展的时代,人工智能尤其是深度学习领域正以惊人的速度改变着我们的世界。从图像识别、语音处理到自然语言处理,深度学习技术在各个领域都取得了显著的成就,为解决复杂的现实问题提供了强大的工具和方法。 PyTorch 是一个…...
若依从redis中获取用户列表
因为若依放入用户的时候,会在减值中添加随机串,所以用户的key会在redis中变成: login_tokens:6af07052-b76d-44dd-a296-1335af03b2a6 这样的样子。 如果用 Set<Object> items redisService.redisTemplate.keys("login_tokens&…...
文件上传之%00截断(00截断)以及pikachu靶场
pikachu的文件上传和upload-lab的文件上传 目录 mime type类型 getimagesize 第12关%00截断, 第13关0x00截断 差不多了,今天先学文件上传白名单,在网上看了资料,差不多看懂了,但是还有几个地方需要实验一下&#…...
Chainlit集成LlamaIndex并使用通义千问实现和数据库交互的网页对话应用(text2sql)
前言 我在之前的几篇文章中写了如何使用Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用,但是发现Langchain的几种和数据库交互的组件都不够让我满意,虽然已经满足了大部分场景的需求,但是问题还是很多,比如…...
计组复习笔记
计组笔记 汇编部分 通用寄存器(General Registers): AX (Accumulator): 用于累加运算,也是乘法和除法的默认寄存器。BX (Base Register): 可以用作一个基址寄存器,通常用于存放数据的基地址。CX (Counter Register): 通常用于循环…...
62. 环境贴图2
环境贴图作用测试 实际生活中光源照射到一个物体上,这个物体反射出去的光线也会影响其他的物体,环境贴图就是用一种简单方式,近似模拟一个物体周边环境对物体表面的影响。 测试:对于PBR材质,如果threejs三维场景不添…...
MATLAB中数据导入与导出的全面指南
在MATLAB中,数据的导入与导出是数据处理工作流中的两个基本步骤。导入是将外部数据加载到MATLAB工作区的过程,而导出则是将工作区中的数据保存到外部文件中。这两个步骤对于数据分析、可视化和结果共享至关重要。本文将详细介绍如何在MATLAB中进行数据的…...
Jenkins从入门到精通,构建高效自动化流程
目录 一、Jenkins简介1、Jenkins的历史与发展(1)Jenkins的起源(2)Jenkins的发展(3)Jenkins的社区与生态系统(4)Jenkins在我国的发展 2、Jenkins的核心功能3、Jenkins的应用场景 二、…...
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
React核心概念:State是什么?如何用useState管理组件自己的数据?
系列回顾: 在上一篇《React入门第一步》中,我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目,并修改了App.jsx组件,让页面显示出我们想要的文字。但是,那个页面是“死”的,它只是静态…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
【从零学习JVM|第二篇】字节码文件
前言: 通过了解字节码文件可以帮助我们更容易的理解JVM的工作原理,所以接下来,我们来介绍一下字节码文件。 目录 前言: 正确的打开字节码文件 字节码文件组成 1. 魔数(Magic Number) 2. 版本号&…...
