协作式 Saga 模式

在 协作式 Saga 模式(Choreographed Saga)中,不同服务之间通过事件进行通信,而没有中央协调者。每个服务都知道自己需要执行哪些操作,并在执行完后发布事件来通知其他服务进行下一步操作。当某个服务执行失败时,其他服务会根据业务需求执行补偿操作,确保最终一致性。

在协作式 Saga 模式中,服务之间是通过事件驱动进行交互的,消息队列(如 RabbitMQ 或 Kafka)用于服务之间传递事件,确保跨服务事务的执行。
协作式 Saga 模式的核心概念

  1. 事件驱动:每个服务根据事件来启动自己的事务,并在事务成功或失败后发布事件通知其他服务。
  2. 无中央协调者:不同服务之间相互了解自己的职责,并且是独立工作的。
  3. 补偿机制:如果某个服务失败,它会发布补偿事件,其他服务根据这些补偿事件执行回滚或补偿操作。

协作式 Saga 模式实现步骤

1. 系统设计

假设有两个服务:

  • Order Service:负责创建订单。
  • Inventory Service:负责扣减库存。

这两个服务之间将通过事件进行通信,确保订单的创建和库存的扣减是可靠的。

2. 技术栈

  • Spring Boot:用于开发微服务。
  • RabbitMQ 或 Kafka:用于事件传递。
  • Spring AMQP 或 Spring Kafka:用于集成消息队列。
  • Spring Data JPA:用于数据库操作。

3. 工作流程

Order Service 接收到订单请求时,创建订单并发布 OrderCreatedEvent,通知其他服务进行下一步操作。
Inventory Service 听到 OrderCreatedEvent 后,执行扣减库存操作。如果成功,它发布 InventoryReservedEvent,通知订单服务库存已预留;如果失败,则发布 InventoryRollbackEvent,通知订单服务进行补偿操作。
补偿服务 听到 InventoryRollbackEvent 后,执行回滚操作,将订单状态设置为“取消”。

4. Spring Boot 示例实现

4.1 Order Service

Order Service 在收到订单创建请求时,首先会创建订单并发布一个事件 OrderCreatedEvent,通知其他服务(如库存服务)进行库存扣减操作。

// OrderService.java
@Service
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Autowired
    private OrderRepository orderRepository;

    // 创建订单
    @Transactional
    public void createOrder(Order order) {
        // Step 1: 创建订单
        orderRepository.save(order);

        // Step 2: 发布事件,通知库存服务进行扣减库存
        OrderCreatedEvent orderCreatedEvent = new OrderCreatedEvent(order.getId(), order.getItemId(), order.getQuantity());
        rabbitTemplate.convertAndSend("orderExchange", "order.created", orderCreatedEvent);
    }
}

4.2 Inventory Service

Inventory Service 监听 OrderCreatedEvent 事件,执行库存扣减操作。如果库存扣减成功,发布 InventoryReservedEvent,否则发布 InventoryRollbackEvent 进行补偿。

// InventoryService.java
@Service
public class InventoryService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private InventoryRepository inventoryRepository;

    // 监听创建订单事件,执行库存扣减
    @RabbitListener(queues = "order.created.queue")
    public void handleOrderCreated(OrderCreatedEvent event) {
        try {
            // Step 1: 扣减库存
            boolean success = decreaseInventory(event.getItemId(), event.getQuantity());
            if (!success) {
                throw new Exception("Insufficient inventory");
            }

            // Step 2: 发布库存预留成功事件
            InventoryReservedEvent reservedEvent = new InventoryReservedEvent(event.getOrderId(), event.getItemId(), event.getQuantity());
            rabbitTemplate.convertAndSend("inventoryExchange", "inventory.reserved", reservedEvent);
        } catch (Exception e) {
            // Step 3: 发布库存扣减失败,回滚事件
            InventoryRollbackEvent rollbackEvent = new InventoryRollbackEvent(event.getOrderId(), event.getItemId(), event.getQuantity());
            rabbitTemplate.convertAndSend("inventoryExchange", "inventory.rollback", rollbackEvent);
        }
    }

    // 扣减库存
    private 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; // 库存扣减成功
    }
}

4.3 补偿操作

如果某个服务执行失败,应该发布补偿事件。InventoryService 如果在扣减库存时失败,将发布 InventoryRollbackEvent 来通知其他服务进行回滚操作。

// CompensationService.java
@Service
public class CompensationService {

    @Autowired
    private OrderRepository orderRepository;

    @RabbitListener(queues = "inventory.rollback.queue")
    public void handleInventoryRollback(InventoryRollbackEvent event) {
        // Step 1: 回滚订单
        Order order = orderRepository.findById(event.getOrderId()).orElseThrow(() -> new RuntimeException("Order not found"));
        order.setStatus("Cancelled");
        orderRepository.save(order);
    }
}

4.4 消息队列配置

配置 RabbitMQ(或 Kafka)交换机和队列:

@Configuration
public class RabbitMQConfig {

    // 创建交换机
    @Bean
    public TopicExchange orderExchange() {
        return new TopicExchange("orderExchange");
    }

    @Bean
    public TopicExchange inventoryExchange() {
        return new TopicExchange("inventoryExchange");
    }

    // 创建队列
    @Bean
    public Queue orderCreatedQueue() {
        return new Queue("order.created.queue");
    }

    @Bean
    public Queue inventoryReservedQueue() {
        return new Queue("inventory.reserved.queue");
    }

    @Bean
    public Queue inventoryRollbackQueue() {
        return new Queue("inventory.rollback.queue");
    }

    // 创建绑定
    @Bean
    public Binding orderCreatedBinding() {
        return BindingBuilder.bind(orderCreatedQueue()).to(orderExchange()).with("order.created");
    }

    @Bean
    public Binding inventoryReservedBinding() {
        return BindingBuilder.bind(inventoryReservedQueue()).to(inventoryExchange()).with("inventory.reserved");
    }

    @Bean
    public Binding inventoryRollbackBinding() {
        return BindingBuilder.bind(inventoryRollbackQueue()).to(inventoryExchange()).with("inventory.rollback");
    }
}

4.5 事件定义

定义事件类,表示不同的操作。

// OrderCreatedEvent.java
public class OrderCreatedEvent {
    private Long orderId;
    private Long itemId;
    private int quantity;

    // 构造函数,getter 和 setter
}

// InventoryReservedEvent.java
public class InventoryReservedEvent {
    private Long orderId;
    private Long itemId;
    private int quantity;

    // 构造函数,getter 和 setter
}

// InventoryRollbackEvent.java
public class InventoryRollbackEvent {
    private Long orderId;
    private Long itemId;
    private int quantity;

    // 构造函数,getter 和 setter
}

5.最终一致性

  • 补偿机制:如果某个服务执行失败,会通过发布补偿事件来保证系统的最终一致性。
  • 幂等性:为了保证补偿事件的幂等性,服务需要确保同一个补偿事件被多次处理时不会产生不一致的状态。例如,库存回滚操作应该是幂等的。
  • 事件的可靠传递:使用可靠的消息队列(如 RabbitMQ 或 Kafka),确保消息不丢失且能够正确投递。

6. 总结

协作式 Saga 模式的核心在于事件驱动和服务间的去中心化协调。每个微服务在收到事件后,执行自己的事务操作,并根据执行结果发布事件或补偿事件,最终通过一系列事件的流转来确保系统的一致性。与编排式 Saga 模式不同,协作式 Saga 模式更加灵活和解耦,但也需要保证事件的可靠性、幂等性以及服务间的正确协调。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于LabVIEW的BeamGage自动化接口应用

设置 National Instruments LabVIEW可执行程序需要被配置为使用.NET 4框架。.NET允许自定义可执行程序的运行方式。可通过以下方式实现&#xff1a; 在LabVIEW安装目录中创建一个名为LabVIEW.exe.config的文本文件&#xff08;例如&#xff1a;C:\Program Files\National Ins…

卸载干净 IDEA(图文讲解)

目录 1、卸载 IDEA 程序 2、注册表清理 3、残留清理 1、卸载 IDEA 程序 点击屏幕左下角 Windows 图标 -> 设置-控制面板->intellij idea 勾选第一栏 Delete IntelliJ IDEA 2022.2 caches and local history&#xff0c;表示同时删除 IDEA 本地缓存以及历史。 Delete I…

李宏毅机器学习课程笔记02 | 机器学习任务攻略General Guide

第一步&#xff1a;分析loss on training data 先检查在训练数据上模型是否很好的学习 情况1&#xff1a;如果在训练集上&#xff0c;loss很大&#xff0c;说明在训练资料上没有训练好 可能性1&#xff1a;设置的模型太简单了&#xff0c;模型存在model bias模型偏差&#x…

【C++】19.多态

文章目录 1. 多态的概念2. 多态的定义及实现2.1 多态的构成条件2.1.1 实现多态还有两个必须重要条件&#xff1a;2.1.2 虚函数 (Virtual Function)定义&#xff1a;特性&#xff1a;示例代码&#xff1a;代码分析1. 类定义部分2. 主函数部分运行结果 重点讲解1. 虚函数的作用2.…

光伏仿真与设计系统应用架构深度剖析

在光伏产业蓬勃发展的时代背景下&#xff0c;绿虫光伏仿真与设计系统成为推动其高效发展的核心力量。其应用架构涵盖多个关键步骤&#xff0c;每个环节都紧密相扣&#xff0c;共同构建起精准且高效的设计体系。 气象分析作为开篇之笔&#xff0c;起着基石般的重要作用。系统全…

进程间通讯

简介&#xff1a; 进程间通讯方式有&#xff1a; 1.内存映射&#xff08;mmap&#xff09;&#xff1a; 使用mmap函数将磁盘空间映射到内存 2.管道 3.信号 4.套接字&#xff08;socket&#xff09; 5.信号机制 通过进程中kill函数&#xff0c;去给另一个函数发送信号&a…

空压机接入配置实例:利用 MODBUS - TCP 转 Ethernet IP 网关实现连接

在工业自动化生产环境中&#xff0c;空压机作为重要的气源设备&#xff0c;其稳定运行和有效监控对于整个生产流程至关重要。然而&#xff0c;不同厂家生产的空压机可能采用不同的通信协议&#xff0c;这给集中监控和管理带来了挑战。在本次案例中&#xff0c;我们遇到的空压机…

基于 Boost.Asio 和 Boost.Beast 的异步 HTTP 服务器(学习记录)

已完成功能&#xff1a; 支持 GET 和 POST 请求的路由与回调处理。 解析URL请求。 单例模式 管理核心业务逻辑。 异步 I/O 技术和 定时器 控制超时。 通过回调函数注册机制&#xff0c;可以灵活地为不同的 URL 路由注册处理函数。 1. 项目背景 1.1 项目简介 本项目是一个基于…

Harmony开发【笔记1】报错解决(字段名写错了。。)

在利用axios从网络接收请求时&#xff0c;发现返回obj的code为“-1”&#xff0c;非常不解&#xff0c;利用console.log测试&#xff0c;更加不解&#xff0c;可知抛出错误是 “ E 其他错误: userName required”。但是我在测试时&#xff0c;它并没有体现为空&#xff0c;…

Spring源码分析之事件机制——观察者模式(二)

目录 获取监听器的入口方法 实际检索监听器的核心方法 监听器类型检查方法 监听器的注册过程 监听器的存储结构 过程总结 Spring源码分析之事件机制——观察者模式&#xff08;一&#xff09;-CSDN博客 Spring源码分析之事件机制——观察者模式&#xff08;二&#xff…

关于Mac中的shell

1 MacOS中的shell 介绍&#xff1a; 在 macOS 系统中&#xff0c;Shell 是命令行与系统交互的工具&#xff0c;用于执行命令、运行脚本和管理系统。macOS 提供了多种 Shell&#xff0c;主要包括 bash 和 zsh。在 macOS Catalina&#xff08;10.15&#xff09;之前&#xff0c…

【C++】20.二叉搜索树

文章目录 1. 二叉搜索树的概念2. 二叉搜索树的性能分析3. 二叉搜索树的插入4. 二叉搜索树的查找5. 二叉搜索树的删除6. 二叉搜索树的实现代码7. 二叉搜索树key和key/value使用场景7.1 key搜索场景&#xff1a;7.2 key/value搜索场景&#xff1a;7.3 主要区别&#xff1a;7.4 ke…

【大模型+本地自建知识图谱/GraphRAG/neo4j/ollama+Qwen千问(或llama3)】 python实战(中)

一、建立基本的知识图谱并导入neo4j 这里我举例用的属性表、关系表&#xff0c;大概格式如下 id名字颜色a1苹果红色 startrelenda1属于b1 启动neo4j&#xff08;关于neo4j的安装此处不再赘述&#xff09; import pandas as pd from py2neo import Graph, Node, Relationship…

【pyqt】(四)Designer布局

布局 之前我们利用鼠标拖动的控件的时候&#xff0c;发现一些部件很难完成对齐这些工作&#xff0c;pyqt为我们提供的多种布局功能不仅可以让排版更加美观&#xff0c;还能够让界面自适应窗口大小的变化&#xff0c;使得布局美观合理。最常使用的三种布局就是垂直河子布局、水…

解决“KEIL5软件模拟仿真无法打印浮点数”之问题

在没有外部硬件支持时&#xff0c;我们会使用KEIL5软件模拟仿真&#xff0c;这是是仿真必须要掌握的技巧。 1、点击“Project”&#xff0c;然后点击“Options for target 项目名字”&#xff0c;点击“Device”,选择CPU型号。 2、点击“OK” 3、点击“Target”,勾选“Use Mi…

【项目实战1】五子棋游戏

目录 C语言编程实现五子棋&#xff1a;&#xff1a; game.h game.c 1.打印菜单 2.打印棋盘 3.玩家下棋 4.判断五子连珠 5.判断输赢 6.游戏运行 game.c完整源代码展示 test.c C语言编程实现五子棋&#xff1a;&#xff1a; game.h #pragma once #include<stdio.h> …

用ResNet50+Qwen2-VL-2B-Instruct+LoRA模仿Diffusion-VLA的论文思路,在3090显卡上训练和测试成功

想一步步的实现Diffusion VLA论文的思路&#xff0c;不过论文的图像的输入用DINOv2进行特征提取的&#xff0c;我先把这个部分换成ResNet50。 老铁们&#xff0c;直接上代码&#xff1a; from PIL import Image import torch import torchvision.models as models from torch…

Spring Boot 项目自定义加解密实现配置文件的加密

在Spring Boot项目中&#xff0c; 可以结合Jasypt 快速实现对配置文件中的部分属性进行加密。 完整的介绍参照&#xff1a; Spring Boot Jasypt 实现application.yml 属性加密的快速示例 但是作为一个技术强迫症&#xff0c;总是想着从底层开始实现属性的加解密&#xff0c;…

A/B实验之置信检验(一):如何避免误判 (I类) 和漏报 (II类)

假设检验的依据&#xff1a;如何避免误判和漏报 A/B实验系列相关文章&#xff08;置顶&#xff09; 1.A/B实验之置信检验&#xff08;一&#xff09;&#xff1a;如何避免误判和漏报 2.A/B实验之置信检验&#xff08;二&#xff09;&#xff1a;置信检验精要 引言 在数据驱动…

每日一题:链表中环的入口结点

文章目录 判断链表环的入口节点描述数据范围&#xff1a;复杂度要求&#xff1a;输入输出 示例代码实现思路解析注意事项&#xff1a; 判断链表环的入口节点 描述 给定一个链表&#xff0c;判断该链表是否存在环。如果存在环&#xff0c;返回环的入口节点&#xff1b;如果不存…