使用Conductor实现微服务架构中Saga模式

在典型的基于微服务的架构中,单个业务用例跨越多个微服务,每个服务都有自己的本地数据存储和本地化事务。当涉及多个事务且微服务数量庞大时,就需要处理跨各种服务的事务。
引入 Saga 模式来处理这些多个事务。它最初由 Hector Garcia Molina 和 Kenneth Salems 于 1987 年提出,被定义为一系列可以相互交错的事务。

在本教程中,我们将深入探讨管理分布式事务的挑战、基于编排的 Saga 模式如何解决这个问题,以及使用 Spring Boot 3 和 Orkes Conductor(领先的企业级版本)实现 Saga 模式的示例。开源编排平台Conductor OSS(以前称为 Netflix Conductor)。

如果分布式事务实施不正确,就会带来很多挑战。在分布式事务中,每个微服务都有一个单独的本地数据库。这种方法通常称为“每个服务数据库”模型。

例如,MySQL 可能因其性能特征和功能而适合一种微服务,而 PostgreSQL 可能因其优势和功能而被选择用于另一种微服务。在此模型中,每个服务执行其本地事务来完成整个应用程序事务。这整个事务称为分布式事务。

分布式事务可以通过多种方式处理。两种传统方法是 2PC(两阶段提交)和 ACID(原子性、一致性、隔离性、持久性)事务,每种方法都有其挑战,例如多语言持久性、最终一致性、延迟等。

什么是Saga模式?
Saga模式是一种用于实现一系列本地事务的架构模式,有助于维护不同微服务之间的数据一致性。

本地事务更新其数据库并通过发布消息或事件来触发下一个事务。如果本地事务失败,saga 会执行一系列补偿事务来回滚先前事务所做的更改。这确保了即使事务失败,系统也能保持一致。

为了进一步说明这一点,请考虑一个订单管理系统,该系统由从下订单到交付订单的连续步骤组成:
在此示例中,该过程从用户从应用程序下订单开始;然后,该流程会经历几个步骤:库存检查、付款处理、运输和通知服务。 
如果支付失败,应用程序必须执行补偿事务来回滚之前步骤中所做的更改,例如撤销支付和取消订单。这确保了 Saga 模式可以处理任何阶段的失败并补偿先前的事务。

Saga 模式可以通过两种不同的方式实现。

  • Choreography编舞:在此模式中,各个微服务使用事件、执行活动并将事件传递给下一个服务。没有集中的协调器,使得服务之间的通信更加困难
  • Orchestration编排:在此模式中,所有微服务都链接到集中协调器,该协调器按预定义的顺序编排服务,从而完成应用程序流程。这有利于可见性、监控和错误处理:

为什么选择基于Orchestration编排的Saga模式?
编排模式中的分散方法使得管理和监控服务交互变得更具挑战性。由于缺乏集中协调和可见性,复杂性增加,使得应用程序更难以维护。
让我们看看 Choreography 的主要缺点以及选择 Orchestration 的优点。

1、Choreography缺点
在构建分布式应用程序时,基于Choreography 的实现有很多限制:

  • 紧耦合——服务是紧密耦合的,因为它们是直接连接的。应用程序中服务的任何更改都可能影响所有连接的服务,因此在升级服务时需要依赖关系。
  • 分布式事实来源——跨各种微服务维护应用程序状态会使流程流的跟踪变得复杂,并且可能需要额外的系统来整合状态信息。这增加了基础设施并给整个系统带来了复杂性。
  • 难以排除故障 – 当应用程序流分布在不同的服务中时,可能需要更长的时间来查找和修复问题。故障排除需要集中的日志服务和对代码的充分理解。如果一项服务出现故障,可能会导致更严重的问题,甚至可能造成大范围的中断。
  • 具有挑战性的测试环境——由于微服务彼此互连,测试对于开发人员来说变得困难。
  • 难以维护——随着服务的发展,合并新版本涉及重新引入条件逻辑,从而再次形成分布式整体。这使得在不检查整个代码的情况下理解服务流变得更加困难。

2、Orchestration优点
在构建分布式应用程序时,基于Orchestration 的实现具有许多优点:

  • 分布式系统内的协调事务——不同的微服务处理分布式系统中事务的不同方面。通过基于编排的模式,中央协调器以预定义的方式管理这些微服务的执行。它主动确保各个本地事务的精确执行,从而保持应用程序的一致性。
  • 补偿事务——在应用程序中,由于任何错误,任何执行点都可能发生故障。Saga 模式允许在发生故障时执行补偿事务。它可以回滚之前完成的事务,确保应用程序保持一致的状态。
  • 异步处理——每个微服务都可以独立处理其活动,集中协调器可以管理这些异步操作的通信和排序。这在特定步骤可能需要较长时间才能完成或需要并行处理的情况下非常有用。
  • 可扩展性——编排模式具有高度可扩展性,这意味着我们可以通过简单地添加或修改所需的服务来对应用程序进行更改,而不会显着影响整个应用程序。这在应用程序需要适应不断变化的需求、允许轻松扩展或修改架构的情况下特别有用。
  • 增强的可见性和监控功能——利用编排模式提供跨分布式应用程序的集中可见性,从而实现快速识别和解决问题。这提高了生产力,最大限度地减少了停机时间,并最终减少了检测故障和从故障中恢复的平均时间。
  • 更快的上市时间——编排器简化了现有服务的重新布线和新流程的创建,促进快速适应。这使得应用程序团队变得更加敏捷,从而加快新想法和概念的上市时间。此外,编排器通常会管理版本控制,从而减少代码中使用大量“if..then..else”语句来创建不同版本的需要。

总之,基于编排的 Saga 模式提供了一种在微服务架构中实现协调、一致和可扩展的分布式事务的方法,并具有通过补偿事务处理故障的额外好处。这使其成为构建健壮且可扩展的分布式应用程序的强大模式。


使用 Orkes Conductor 实现 Saga 编排模式
现在,让我们看一下使用 Saga 模式和Orkes Conductor 的应用程序的实际示例。

考虑一个具有以下服务的订单管理系统:

  • OrderService – 处理初始订单放置,包括将商品添加到购物车、指定数量以及初始化结账流程。
  • InventoryService – 检查并确认物品的可用性。
  • PaymentService – 安全地管理支付流程,处理各种支付方式。
  • ShipmentService – 准备运输物品,包括包装、生成运输标签和启动运输流程。
  • NotificationService – 向用户发送有关订单更新的通知。

让我们探索使用 Orkes Conductor 和 Spring Boot 3 复制此流程。

在开始应用程序开发之前,请确保系统满足以下先决条件。

  • 安装Java 17

要为我们的应用程序设置 Orkes Conductor,我们可以选择以下任意方法:
  • 本地设置 Conductor


以下是使用 Saga 模式构建的送餐应用程序的代码片段:

@AllArgsConstructor
@Component
@ComponentScan(basePackages = {"io.orkes"})
public class ConductorWorkers {
    
    @WorkerTask(value =
"order_food", threadCount = 3, pollingInterval = 300)
    public TaskResult orderFoodTask(OrderRequest orderRequest) {
        String orderId = OrderService.createOrder(orderRequest);
        TaskResult result = new TaskResult();
        Map<String, Object> output = new HashMap<>();
        if(orderId != null) {
            output.put(
"orderId", orderId);
            result.setOutputData(output);
            result.setStatus(TaskResult.Status.COMPLETED);
        } else {
            output.put(
"orderId", null);
            result.setStatus(TaskResult.Status.FAILED);
        }
        return result;
    }
}


让我们看看工作流程是如何进行的:

  • 当用户在食品配送应用程序上下订单时,该应用程序就会启动。初始流程作为一系列工作任务实现,包括将食物添加到购物车 ( order_food )、检查餐厅的食物供应情况 ( check_inventory )、付款流程 ( make_ payment ) 和配送流程 ( Ship_food )。
  • 然后应用程序流程继续进行fork-join 任务,该任务处理通知服务。它有两个叉子,一个用于通知送货员,另一个用于通知用户。

以下是食品配送应用程序补偿事务:

在 Orkes Conductor 中定义工作流时,我们可以 在主应用程序失败时触发failureWorkflow。在定义中,包含在应用程序失败时要运行的工作流名称。
"failureWorkflow": "<name of the workflow to be run on failure>",
Orkes Conductor 中的补偿工作流程会在发生故障时回滚更改

结论
在本文中,我们使用 Orkes Conductor 和 Java Spring Boot 3 成功开发了一个订单管理应用程序,实现了 Saga 模式。

github项目