【Spring Boot】微服务架构下Saga模式的实战解析

现代微服务虽小巧、自治且能独立部署------但这恰恰埋下了一个大问题。

想想那些典型的药房处方管理、支付、银行、物流、供应链或预订系统吧,它们的操作往往横跨多个微服务

  • 创建订单
  • 预留库存
  • 处理支付
  • 创建发货单
  • 更新积分

每个服务都运行在各自独立的数据库服务网络边界内。

如果某个操作半路掉链子,整个业务流程绝不能把系统丢在一个不一致的状态里不管。

这时候,Saga 模式就变得至关重要了。

Saga 是什么?为什么不用 ACID 事务?

一个 Saga 就是一串本地事务的集合。每个本地事务更新单个服务内的数据,并发布一个事件来触发下一步。

一旦发生失败:

  • Saga 就会启动补偿操作,回滚前面已经完成的步骤。

为什么 ACID(两阶段提交,2PC)在微服务里玩不转?

问题 解释
2PC 是阻塞的 要是协调者崩溃,整个系统就卡住了。
微服务各有独立的数据库 分布式数据库之间没有共享的提交日志。
扩展性差 锁会蔓延到整个分布式系统。
云服务普遍不支持 XA 事务 DynamoDB, S3, Mongo, Kafka, RDS 等等都不支持。

Saga 通过事件驱动或集中编排的补偿逻辑,巧妙地避开了这些问题。

Saga 架构图

1. 实战用例:药房处方订单处理

场景

想象一个在线药房处方平台,客户下单购买一个产品。

这个系统涉及三个独立服务:

  1. 订单服务 ------ 创建订单记录。
  2. 库存服务 ------ 预留库存中的产品。
  3. 支付服务 ------ 向客户收款。

没有 Saga 会怎样?

如果其中任何一步失败(比如支付失败),前面步骤可能导致系统状态不一致:

  • 订单已创建
  • 库存已预留
  • 支付失败
  • 结果:库存被卡住,订单不完整,需要人工干预。

这在企业系统中是绝对无法接受的,尤其是在多个微服务交互的场景下。

Saga 的解决方案

使用 Saga 编排模式 ,每个服务执行自己的本地事务 ,一旦失败,就通过补偿操作回滚前面的步骤:

  • 步骤 1: 订单服务创建订单。
  • 步骤 2: 库存服务预留库存。
  • 步骤 3: 支付服务向客户收款。
  • 失败处理: 如果支付失败,编排器会调用库存服务释放库存。

这确保了跨服务的业务级别的一致性

2. 为什么我们需要 Saga 模式?

  1. 分布式微服务: 传统的 ACID 事务无法跨越多个微服务。
  2. 最终一致性: 确保最终结果一致,无需全局资源锁。
  3. 容错性: 如果后续步骤失败,能自动回滚前面的步骤。
  4. 云原生: 能与云环境中可扩展的服务无缝协作。

松耦合:每个服务保持独立,仅通过 API 通信。

3. 使用 Saga 的好处

  • 可靠性: 补偿事务防止数据不一致。
  • 可扩展性: 每个服务可以独立扩展;编排是集中化的。
  • 韧性: 优雅地处理部分故障。
  • 业务级别的一致性: 关注正确的业务结果,而非严格的数据库一致性。
  • 监控与调试: 中央编排器为每个事务步骤提供清晰的日志。

4. 代码示例逐步解析

下面是对你的 Spring Boot Saga 项目中关键脚本的详细解读

4.1 订单模型 (Order.java)

java 复制代码
package com.example.orderservice;
public class Order {
    private Long id;
    private String product;
    private String status;
    public Order() {
        // default constructor (required for Jackson)
    }
    public Order(Long id, String product, String status) {
        this.id = id;
        this.product = product;
        this.status = status;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getProduct() {
        return product;
    }
    public void setProduct(String product) {
        this.product = product;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
}
  • 用途: 代表一个订单。
  • 业务逻辑: 存储产品名称、订单ID和状态(CREATED,CANCELLED,COMPLETED)。

4.2 订单控制器 (OrderController.java)

java 复制代码
package com.example.orderservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderController {
    @PostMapping
    public Order create(@RequestParam("product") String product) {
        return new Order(1L, product, "CREATED");
    }
    @PostMapping("/cancel")
    public String cancel() {
        return "ORDER_CANCELLED";
    }
    @PostMapping("/complete")
    public String complete() {
        return "ORDER_COMPLETED";
    }
}
  • 端点
    • POST /orders → 创建订单。
    • POST /orders/cancel → 取消订单(用于补偿)。
    • POST /orders/complete → 标记订单完成。
  • 为何适用于 Saga: 端点简单、可预测,并返回对象或状态字符串供编排器使用。

4.3 库存控制器 (InventoryController.java)

java 复制代码
package com.example.inventoryservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/inventory")
public class InventoryController {
    @PostMapping("/reserve")
    public boolean reserve(@RequestParam("product") String product) {
        return !"FAIL".equals(product);
    }
    @PostMapping("/release")
    public void release(@RequestParam("product") String product) {}
}
  • 用途: 预留产品库存,如果支付失败则释放它。
  • 逻辑: 模拟成功/失败。在企业系统中,这会检查数据库中的实际库存。

4.4 支付控制器 (PaymentController.java)

java 复制代码
package com.example.paymentservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/payments")
public class PaymentController {
    @PostMapping("/pay")
    public boolean pay(@RequestParam("amount") double amount) {
        return amount <= 5000;
    }
    @PostMapping("/refund")
    public void refund(@RequestParam double amount) {}
}
  • 用途: 处理支付,并模拟失败以进行测试。
  • 为何适用于 Saga: 返回布尔值指示成功/失败,允许编排器触发补偿操作。

4.5 Saga 编排器 (SagaController.java)

java 复制代码
package com.example.sagaorchestrator;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/saga")
public class SagaController {
    private final RestTemplate rest = new RestTemplate();
    @PostMapping("/order")
    public String place(@RequestParam("product") String product, @RequestParam("amount") double amount) {
        Object order = rest.postForObject(
                "http://localhost:8081/orders?product=" + product,
                null,
                Object.class
        );
        
        if (order == null) {
            return "Order creation failed";
        }
        Boolean inventory = rest.postForObject(
            "http://localhost:8082/inventory/reserve?product=" + product,
            null,
            Boolean.class
        );
        if (inventory == null || !inventory) {
            return "Inventory failed";
        }
        Boolean payment = rest.postForObject(
            "http://localhost:8083/payments/pay?amount=" + amount,
            null,
            Boolean.class
        );
        if (payment == null || !payment) {
            rest.postForObject(
                    "http://localhost:8082/inventory/release?product=" + product,
                    null,
                    Void.class
            );
            return "Payment failed, compensated";
        }
        return "Order completed successfully";
    }
}
  • 流程解析
    1. 订单创建 → 第一步,必须成功才能继续。
    2. 库存预留 → 如果失败,Saga 停止。
    3. 支付处理 → 如果失败,编排器触发库存释放(补偿)。
    4. 成功 → 返回成功消息。

企业级就绪: 演示了清晰的编排、补偿和业务级别的一致性

4.6 输出验证

  1. 成功案例

  2. 支付失败案例

  3. 库存失败案例

5. 核心优势总结

  • 容错性强: 自动补偿失败的事务。
  • 业务一致性: 确保订单状态和库存始终保持一致。
  • 易于扩展: 每个服务独立运行。
  • 云原生就绪: 可在 Kubernetes 或任何云环境中运行。
  • 企业适用性强: 符合医疗、金融、数据平台等真实世界系统的需求。

6. 企业为何需要这个模式?

  • 在分布式系统中, 手动回滚容易出错。
  • Saga 确保自动恢复,减少停机时间。
  • 防止多服务工作流中出现数据不一致。
  • 支持异步处理,提升性能和可扩展性。
相关推荐
上海云盾第一敬业销售2 小时前
高防IP架构解析与实践分享
网络协议·tcp/ip·架构
h7ml2 小时前
基于 JPA 和多租户架构支持多企业微信账号的 SaaS 后端设计
架构·企业微信
小北方城市网2 小时前
数据库性能优化实战指南:从索引到架构,根治性能瓶颈
数据结构·数据库·人工智能·性能优化·架构·哈希算法·散列表
何中应2 小时前
使用Spring自带的缓存注解维护数据一致性
java·数据库·spring boot·后端·spring·缓存
heartbeat..2 小时前
Spring Boot 学习:原理、注解、配置文件与部署解析
java·spring boot·学习·spring
zyxzyx492 小时前
从 Transformer 架构看 AI 提效:任务拆解为何能激活大模型的推理能力?
人工智能·架构·transformer
一路向北⁢2 小时前
企业级敏感词拦截检查系统设计方案(Spring Boot)
spring boot·后端·bootstrap·敏感词·敏感词拦截
吴巴格2 小时前
springboot引用其他中间件,如何确定版本
spring boot·后端·中间件