编程

微服务中的 Saga 模式

662 2024-09-30 01:47:00

1. 概述

从其核心原则和真实上下文来看,基于微服务的应用是一个分布式系统。整个系统由多个较小的服务组成,这些服务共同提供整体应用程序功能。

虽然这种架构风格提供了许多好处,但它也有一些局限性。微服务架构中的一个主要问题是如何处理跨多个服务的事务。

本文中,我们将探索 Saga 架构模式,该模式允许我们在微服务架构中管理分布式事务。

2. 每个服务模式的数据库

微服务架构的一个好处是,我们可以为每个服务选择合适的技术栈。

例如,我们可以让服务 A 使用关系数据库,让服务 B 使用 NoSQL 数据库。

这样的模型允许服务在最适合其数据类型和 schema 的数据存储上独立管理域数据。此外,它还允许服务按需扩展其数据存储,并将其与其他服务的故障隔离开来。

然而,有时一个事务可以跨越多个服务,确保整个服务数据库的数据一致性是一个挑战。在下一节中,我们将通过一个例子更深入地了解分布式事务管理的挑战。
3. 分布式事务

为了演示分布式事务的使用,我们将以一个处理在线订单并采用微服务架构实现的电商应用为例。

有一个微服务来创建订单,一个处理付款,另一个更新库存,最后一个交付订单。

这些微服务中的每一个都执行一个本地事务来实现各个功能:

这是一个分布式事务的示例,因为事务边界跨越多个服务和数据库。

为了确保订单处理服务的成功,所有四个微服务都必须完成单独的本地事务。如果任何微服务未能完成其本地事务,则所有已完成的先前事务都应回滚以确保数据完整性。

4. 分布式事务面临的挑战

在上一节中,我们提供了一个分布式事务的真实示例。微服务架构中的分布式事务带来了两个关键挑战。

第一个挑战是维护 ACID。为了确保事务的正确性,它必须保证原子性、一致性、隔离性和持久性(ACID)。原子性确保事务的所有步骤都完成或者所有步骤都没有完成。一致性将数据从一个有效状态转换为另一个有效的状态。隔离保证并发事务应产生与顺序事务相同的结果。最后,持久性意味着无论任何类型的系统故障如何,已提交的事务都会保持提交状态。在分布式事务场景中,由于事务跨越多个服务,确保 ACID 始终是关键。

第二个挑战是管理事务隔离级别。它指定了当其他服务同时访问相同数据时,事务中可见的数据量。换句话说,如果一个微服务中的一个对象在数据库中持久化,而另一个请求读取数据,服务应该返回旧数据还是新数据?

5. 理解两阶段提交

两阶段提交协议(2PC)是一种广泛用于实现分布式事务的模式。我们可以在微服务架构中使用这种模式来实现分布式事务。

在两阶段提交协议中,有一个协调器组件负责控制事务,并包含管理事务的逻辑。

另一个组件是运行本地事务的参与节点(例如微服务):

顾名思义,两阶段提交协议分两个阶段运行分布式事务:

  1. 准备阶段 – 协调器(coordinator)询问参与的节点是否准备好提交事务。参与者回答“yes”或“no”。
  2. 提交阶段 – 如果所有参与节点在阶段 1 中都做出了肯定的响应,则协调器会要求所有节点提交。如果有任何一个节点返回否,协调器会要求所有参与者回滚其本地事务。

6. 两阶段提交存在的问题

尽管 2PC 对于实现分布式事务很有用,但它有以下缺点:

  • 事务的责任在协调器节点上,它可能成为单点故障。
  • 所有其他服务都需要等待,直到最慢的服务完成确认。因此,事务的整体性能受到最慢服务的限制。

由于对协调器有对话需要和依赖性,两阶段提交协议的设计成较慢。因此,在涉及多个服务的基于微服务的架构中,它可能会导致可扩展性和性能问题。
NoSQL 数据库不支持两阶段提交协议。因此,在一个或多个服务使用 NoSQL 数据库的微服务架构中,我们不能应用两阶段提交。

7. 引入 Saga

7.1.什么是 Saga 架构模式?

Saga 架构模式使用一系列本地事务提供事务管理

本地事务是 Saga 参与者执行的工作单位。Saga的 每一项操作都可以通过补偿性事务来回滚。此外,Saga 模式保证所有操作都能成功完成,或者运行相应的补偿事务以撤消之前完成的工作。

在 Saga 模式中,补偿事务必须幂等且可重试。这两个原则确保我们可以在没有任何人工干预的情况下管理事务。

Saga 执行协调器(SEC)保证这些原则:

上图显示了如何可视化我们之前讨论的在线订单处理场景的 Saga 模式。

7.2. Saga 执行协调器

Saga 执行协调器(Saga Execution Coordinator)是实现 Saga 流程的核心组件。它包含一个 Saga 日志,用于捕获分布式事务的事件序列。

对于任何故障,SEC 组件都会检查 Saga 日志,以确定受影响的组件以及补偿事务应运行的顺序。

对于 SEC 组件中的任何故障,它可以在恢复后读取 Saga 日志。

然后,它可以识别成功回滚的事务,哪些事务处于挂起状态,并可以采取适当的操作:

实现 Saga 模式有两种方法:协调(choreography)和编排(orchestration)。我们将在下一节中讨论。

7.3. 实现 Saga 协调模式

在 Saga 编排模式中,作为事务一部分的每个微服务都会发布一个由下一个微服务处理的事件

要使用这种模式,我们需要决定微服务是否将成为 Saga 的一部分。因此,微服务需要使用适当的框架来实现 Saga。在这种模式中,Saga 执行协调器要么嵌入在微服务中,或者可以是一个独立的组件。

在 Saga 中,如果所有微服务都完成了本地事务,并且没有一个微服务报告任何故障,则协调流是成功的。

下图展示了在线订单处理应用的成功 Saga 流程:

如果发生故障,微服务会向 SEC 报告故障,SEC 有责任调用相关的补偿性事务

本例中,Payment 微服务报告了一个故障,SEC 调用补偿性事务来解锁席位。如果对补偿性事务的调用失败,SEC 有责任重试,直到成功完成。回想一下,在 Saga 中,补偿性事务必须是幂等且可重试的。

编排模式适用于绿地(即还未开发的)微服务应用的开发。此外,当交易中的参与者较少时,这种模式也适用。

以下是一些可用于实现编排模式的框架:

  • Axon Saga – 一个轻量级的框架,广泛用于基于Spring Boot的微服务
  • Eclipse MicroProfile LRA – 基于 REST 原则的 Saga HTTP 传输分布式事务的实现
  • Eventuate Tram Saga – 基于 Spring Boot 和 Micronaut 的微服务的 Saga 编排框架
  • Seata – 具有高性能和易于使用的分布式事务服务的开源分布式事务框架

7.4. 实现 Saga 编排模式

在编排(Orchestration)模式中,有一个编排器负责管理整体事务状态

如果任何微服务遇到故障,编排器负责调用必要的补偿事务:

Saga 编排模式对于棕地(即已有的)微服务应用开发架构非常有用。换句话说,当我们已经有了一组微服务,并希望在应用中实现 Saga 模式时,这种模式是有效的。我们需要定义适当的补偿交易来继续这种模式。

以下是一些可用于实现编排器模式的框架:

  • Camunda 是一个基于 Java 的框架,支持用于工作流和流程自动化的业务流程模型和表示法(Business Process Model And Notation,BPMN)标准。
  • Apache Camel 提供 Saga 企业集成模式(EIP)的实现。

8. 小结

本文中,我们讨论了在基于微服务的应用中实现分布式事务的 Saga 架构模式。

首先,我们介绍了这些实现的所面临的挑战。

然后探索了 Saga 的一种流行替代方案——两阶段提交协议,并研究了它在基于微服务的应用中实现分布式事务的局限性。

最后,我们讨论了 Saga 架构模式,它是如何工作的,以及在基于微服务的应用中实现 Saga 模式的两种主要方法。