编排式 Saga 模式
编排式 Saga 模式(Orchestrated Saga)是指由一个中央协调者(Orchestrator)控制多个服务间的事务执行。与协作式 Saga 模式不同,编排式 Saga 模式不依赖于事件驱动,而是通过协调者来控制整个 Saga 流程的执行。协调者负责调用各个参与服务,确保每个子事务按顺序执行,并在某个子事务失败时触发补偿操作。
编排式 Saga 模式实现步骤
我们将实现一个基于编排式 Saga 模式的跨服务数据保存方案,使用 Spring Boot 来开发微服务,使用 RabbitMQ 或 Kafka 作为消息队列进行通信,并通过一个中央协调者来管理整个 Saga 流程。
以下是如何使用编排式 Saga 模式来实现一个典型的跨服务操作:订单创建和库存扣减。
1. 架构设计
我们有两个微服务:
- Order Service:负责创建订单。
- Inventory Service:负责管理库存。
- Saga Orchestrator Service:协调整个 Saga 流程,包括执行各服务事务并在失败时触发补偿操作。
2. 技术栈
- Spring Boot:用于开发微服务。
- Spring Cloud:用于服务注册、发现和治理。
- Spring AMQP / Kafka:用于服务间消息传递(可选择 RabbitMQ 或 Kafka)。
- Spring Data JPA:用于数据库操作。
- Transactional Outbox Pattern:用来确保跨服务操作的一致性。
3. 系统流程
- Order Service:接收创建订单请求,调用 Saga Orchestrator Service 开始 Saga 流程。
- Saga Orchestrator:协调 Inventory Service 扣减库存,等到确认成功后,继续后续操作(如创建订单)。
- Inventory Service:接收扣减库存请求,执行库存扣减,如果成功,通知 Saga Orchestrator。如果失败,则触发补偿操作。
- 补偿操作:如果任何一个服务的事务失败,Saga Orchestrator 会调用补偿操作回滚之前的事务,确保最终一致性。
4. Spring Boot 示例实现
4.1 创建 Order Service
Order Service 负责处理订单请求,并与 Saga Orchestrator 配合,触发 Saga 流程。
// OrderService.java
@Service
public class OrderService {@Autowiredprivate SagaOrchestrator sagaOrchestrator;// 创建订单@Transactionalpublic void createOrder(Order order) {// Step 1: 创建订单orderRepository.save(order);// Step 2: 调用 Saga Orchestrator 开始整个流程sagaOrchestrator.startSaga(order);}
}
4.2 创建 Saga Orchestrator Service
Saga Orchestrator Service 是整个 Saga 模式的核心,它负责协调各个服务之间的事务执行。首先,它会启动 Saga 事务,接着协调 Inventory Service 执行库存扣减操作,并处理补偿操作。
// SagaOrchestrator.java
@Service
public class SagaOrchestrator {@Autowiredprivate InventoryService inventoryService;@Autowiredprivate OrderRepository orderRepository;// 启动 Saga 流程@Transactionalpublic void startSaga(Order order) {try {// Step 1: 调用库存服务扣减库存boolean inventorySuccess = inventoryService.decreaseInventory(order.getItemId(), order.getQuantity());if (!inventorySuccess) {throw new Exception("Inventory insufficient");}// Step 2: 库存扣减成功后,继续创建订单order.setStatus("Created");orderRepository.save(order);} catch (Exception e) {// Step 3: 如果出错,执行补偿操作compensate(order);}}// 补偿方法,回滚库存操作private void compensate(Order order) {// 回滚库存,增加库存inventoryService.rollbackInventory(order.getItemId(), order.getQuantity());// 回滚订单,设置订单为失败状态order.setStatus("Failed");orderRepository.save(order);}
}
4.3 创建 Inventory Service
Inventory Service 负责扣减库存并通知 Saga Orchestrator 执行后续操作。
// InventoryService.java
@Service
public class InventoryService {@Autowiredprivate InventoryRepository inventoryRepository;// 扣减库存@Transactionalpublic boolean decreaseInventory(Long itemId, int quantity) {Inventory inventory = inventoryRepository.findByItemId(itemId);if (inventory.getStock() < quantity) {return false; // 库存不足}inventory.setStock(inventory.getStock() - quantity);inventoryRepository.save(inventory);return true; // 库存扣减成功}// 补偿操作,回滚库存@Transactionalpublic void rollbackInventory(Long itemId, int quantity) {Inventory inventory = inventoryRepository.findByItemId(itemId);inventory.setStock(inventory.getStock() + quantity); // 恢复库存inventoryRepository.save(inventory);}
}
4.4 消息队列(RabbitMQ 或 Kafka)集成
为了实现 Saga 模式的跨服务通信,我们可以使用消息队列来传递消息。这里我们使用 RabbitMQ 作为消息队列。
在 application.properties 中配置 RabbitMQ:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
在 SagaOrchestrator 和 InventoryService 中,我们可以通过 RabbitTemplate 来发送和接收消息。
// SagaOrchestrator.java
@Autowired
private RabbitTemplate rabbitTemplate;// 启动 Saga 流程时,发送事件
public void startSaga(Order order) {// 发送一个消息,通知库存服务处理库存rabbitTemplate.convertAndSend("inventoryExchange", "inventory.decrease", order);
}// 监听库存扣减消息的回调
@RabbitListener(queues = "inventory.decrease.queue")
public void handleInventoryDecrease(Order order) {try {// 扣减库存并继续订单处理boolean inventorySuccess = inventoryService.decreaseInventory(order.getItemId(), order.getQuantity());if (!inventorySuccess) {throw new Exception("Inventory insufficient");}// 订单处理继续order.setStatus("Created");orderRepository.save(order);} catch (Exception e) {// 执行补偿操作compensate(order);}
}
4.5 设置消息队列的交换机和队列
@Configuration
public class RabbitMQConfig {@Beanpublic TopicExchange inventoryExchange() {return new TopicExchange("inventoryExchange");}@Beanpublic Queue inventoryDecreaseQueue() {return new Queue("inventory.decrease.queue");}@Beanpublic Binding inventoryDecreaseBinding() {return BindingBuilder.bind(inventoryDecreaseQueue()).to(inventoryExchange()).with("inventory.decrease");}@Beanpublic Jackson2JsonMessageConverter jackson2JsonMessageConverter() {return new Jackson2JsonMessageConverter();}@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,Jackson2JsonMessageConverter jackson2JsonMessageConverter) {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);return rabbitTemplate;}
}
5. 确保最终一致性
在编排式 Saga 模式中,每个服务通过本地事务来保证操作的原子性,并通过协调者来确保每个子事务执行成功。当某个服务失败时,协调者会触发补偿操作回滚之前的操作。关键要素是:
补偿操作:服务必须提供回滚或补偿机制,确保在失败时能够撤销已完成的事务。
幂等性:补偿操作应该是幂等的,确保多次执行不会产生不一致的结果。
6. 总结
编排式 Saga 模式通过中央协调者来管理跨服务事务,确保最终一致性和数据可靠性。使用 RabbitMQ 或 Kafka 进行服务间的消息通信,可以将系统解耦,提高扩展性。在这种模式下,协调者充当了服务之间的桥梁,负责事务流的管理,并在必要时执行补偿操作。
相关文章:
编排式 Saga 模式
编排式 Saga 模式(Orchestrated Saga)是指由一个中央协调者(Orchestrator)控制多个服务间的事务执行。与协作式 Saga 模式不同,编排式 Saga 模式不依赖于事件驱动,而是通过协调者来控制整个 Saga 流程的执行…...
QT 下拉菜单设置参数 起始端口/结束端口/线程数量 端口扫描4
上篇文章QT实现 端口扫描暂停和继续功能 3-CSDN博客 双击 添加对话框类 界面设计 由于主体代码已经写完,只需要更改参数的获取即可 获取起始端口结束端口的输入 槽函数 给主界面类添加调用对话框类的功能 实现功能:点击菜单项可以弹出对话框窗体 增加槽…...
缓存-Redis-常见问题-缓存击穿-永不过期+逻辑过期(全面 易理解)
缓存击穿(Cache Breakdown) 是在高并发场景下,当某个热点数据在缓存中失效或不存在时,瞬间大量请求同时击中数据库,导致数据库压力骤增甚至崩溃的现象。为了解决这一问题,“永不过期” “逻辑过期” 的策略…...
137. 只出现一次的数字 II
137. 只出现一次的数字 II 题目-中等难度1. 位运算2. 位运算 题目-中等难度 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数…...
【力扣热题100】—— Day18.将有序数组转换为二叉搜索树
期末考试完毕,假期学习开始! —— 25.1.7 108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵平衡二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9] …...
PyTorch 官方文档 中文版本
文档来源 https://pytorch.cadn.net.cn 大多数机器学习工作流都涉及处理数据、创建模型、优化模型 参数,并保存经过训练的模型。本教程向您介绍完整的 ML 工作流 在 PyTorch 中实现,并提供了用于了解有关每个概念的更多信息的链接。 我们将使用 Fashion…...
电力智能问答RAG: 多问题生成、思维链提示生成;混合编码和重排序策略
电力智能问答RAG 目录 电力智能问答RAG文档转换、元信息抽取与增强及文档解析模块多问题生成、思维链提示生成和指令微调数据集构建模块混合编码和重排序策略文档转换、元信息抽取与增强及文档解析模块 在电力领域的知识处理中,文档转换、元信息抽取与增强及文档解析模块发挥…...
C#高级:递归4-根据一颗树递归生成数据列表
一、目的 该程序展示了如何将树形结构的数据(例如家庭成员信息)转化为一维列表形式,以便于存储、展示或操作。 二、流程思路 创建树:首先通过 GetDemoTree 创建一个简单的家庭树,树的根节点是“爸爸”,然…...
PDFelement 特别版
Wondershare PDFelement Pro 是一款非常强大的PDF编辑软件,它允许用户轻松地编辑、转换、创建和管理PDF文件。这个中文特别版的软件具有许多令人印象深刻的功能,PDFelement Pro 提供了丰富的编辑功能,可以帮助用户直接在PDF文件中添加、删除、…...
云计算在医疗行业的应用
云计算在医疗行业的应用广泛而深入,为医疗服务带来了前所未有的变革。以下是对云计算在医疗行业应用的详细解析: ### 一、医疗数据共享与整合 云计算平台具有强大的数据存储和处理能力,使得医疗数据共享与整合成为可能。通过云计算平台&…...
(转)rabbitmq怎么保证消息不丢失?
RabbitMQ 可以通过以下多种机制来保证消息不丢失: 生产阶段 - 持久化队列和交换器: - 在声明队列和交换器时,将 durable 参数设置为 true ,确保它们是持久化的。这样,即使 RabbitMQ 节点重新启动,队列和交…...
每日一题:链表中环的入口结点
文章目录 判断链表环的入口节点描述数据范围:复杂度要求:输入输出 示例代码实现思路解析注意事项: 判断链表环的入口节点 描述 给定一个链表,判断该链表是否存在环。如果存在环,返回环的入口节点;如果不存…...
k8s里面etcd的作用
etcd 是 Kubernetes 集群中一个至关重要的组件,它是一个开源的分布式键值存储系统,主要用于存储和管理 Kubernetes 集群的配置和状态信息。以下是 etcd 在 Kubernetes 中的具体作用和功能: ### 1. **集群状态存储** etcd 是 Kubernetes 集群的持久化存储后端,负责存储和管…...
使用 uniapp 开发微信小程序遇到的坑
0. 每次修改代码时,都会触发微信开发工具重新编译 终极大坑,暂未找到解决方案 1. input 无法聚焦问题 问题:在小程序开发工具中,input 会突然无法聚焦,重启也不行。但是真机调试可以正常聚焦。 解决办法:…...
AlphaPi相关硬件驱动提取
初涉硬件编程,在咸鱼上搞了几块AlphaPi和microbit的板鼓捣了一下,alphapi生态不完善,网上又无任何文档,搞封闭,可玩性实在有限,但貌似相关扩展板是可以插microbit的,于是想把这些扩展版用microb…...
【学习笔记】数据结构(十)
内部排序 文章目录 内部排序10.1 概述10.2 插入排序10.2.1 直接插入排序10.2.2 其他插入排序10.2.2.1 折半插入排序(Binary Insertion Sort)10.2.2.2 2-路插入排序(Two-Way Insertion Sort)10.2.2.3 表插入排序(Table Insertion Sort…...
Unity中 Xlua使用整理(二)
1.Xlua的配置应用 xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。配置要求: 列表方式均必须是static的字段/属性 列表方式均必须放到一个static类 建议不用标签方式 建议列表方式配置放Editor目录(如果是H…...
刚体变换矩阵的逆
刚体运动中的变换矩阵为: 求得变换矩阵的逆矩阵为: opencv应用 cv::Mat R; cv::Mat t;R.t(), -R.t()*t...
高等数学-----极限、函数、连续
考研数学笔记...
ubuntu 创建服务、查看服务日志
1. 在 /etc/systemd/system/ 下创建文件,名称为 xxx.service [Unit] DescriptionYour Service Description Afternetwork.target[Service] Typesimple ExecStart/path/to/your/service/executable Restarton-failure[Install] WantedBymulti-user.target2. 配置服务…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
