微服务中如何使用API组合模式进行查询?

本文选自 《微服务架构设计模式》 一书。

在微服务架构中编写查询非常具有挑战性。查询通常需要检索分散在多个服务所拥有的数据库中的数据。但是,你不能使用传统的分布式查询处理机制,因为即使技术上可行,它也会打破服务之间的隔离和封装。下面将介绍一种在微服务架构中实现查询操作的最简单方法——API组合模式。

什么是 API 组合模式

这个模式通过调用拥有数据的服务并组合结果来实现查询操作。下图显示了该模式的结构。它有两种类型的参与者:

  1. API组合器:它通过查询数据提供方的服务来实现查询操作。
  2. 数据提供方服务:拥有查询返回的部分数据的服务。

上图显示了三个提供方服务。API 组合器通过从提供方服务检索数据并组合结果来实现查询。API 组合器可能是需要数据呈现网页的客户端,例如 Web 应用程序。或者,它可能是一个服务,例如 API Gateway 及后端前置模式 ,这个模式将查询操作公开为 API 接口。

微服务中如何使用API组合模式进行查询?

是否可以使用此模式实现特定查询操作取决于几个因素,包括数据的分区方式、拥有数据的服务公开的 API 的功能,以及服务使用数据库的功能,等等。例如,即使提供方服务拥有用于检索所需数据的 API,聚合器也可能需要执行大量数据集的低效内存连接。稍后,你将看到使用此模式无法实现查询操作的例子。但幸运的是,有许多场景可以应用这种模式。为了看到它的实际效果,我们来举个例子。

使用 API 组合模式实现 findOrder() 查询操作

findOrder() 查询操作相当于一个简单的基于主键的 equi-join 查询。可以假设每个提供方服务都有一个 API接口,用于通过 orderId 检索所需的数据。因此,采用 API 组合模式来实现 findOrder() 查询操作似乎是合情合理的。API 组合器调用四个服务并将结果组合在一起。下图显示了Find Order Composer的设计。

微服务中如何使用API组合模式进行查询?

显而易见,API 组合模式非常简单。让我们看一下在应用此模式时必须解决的几个设计问题。

API 组合模式的设计缺陷

使用此模式时,你必须解决两个设计问题:

  • 确定架构中的哪个组件是查询操作的 API 组合器。
  • 如何编写有效的聚合逻辑。

让我们看看每个问题。

由谁来担任 API 组合器的角色

这是你必须做出的一个决定,选择由谁来扮演查询操作的 API 组合器这个角色。你有三个选择。第一个选择,如下图所示,是由服务的客户端扮演 API 组合器的角色。

微服务中如何使用API组合模式进行查询?

实现 Order Status 视图并在同一局域网上运行的前端客户端(如 Web 应用程序)可以使用此模式有效地检索订单详细信息。对于防火墙之外的客户以及通过较慢网络访问的服务,此选择可能不实用。

第二个选择如下图所示,由实现应用程序外部 API 的 API Gateway 来扮演 API 组合器的角色,用来完成查询操作和查询结果的组合。

微服务中如何使用API组合模式进行查询?

如果查询操作是应用程序外部 API 的一部分,则此选择有意义。API Gateway 不是将请求路由到另一个服务,而是实现 API 组合逻辑。这种方法使得在防火墙外运行的客户端(例如移动设备)能够通过单个 API 调用有效地从众多服务中检索数据。

第三个选择如下图所示,是将 API 组合器实现为独立的服务。

微服务中如何使用API组合模式进行查询?

此选择可以用于由多个服务在内部使用的查询操作。此操作还可用于外部可访问的查询操作,由于它们的聚合逻辑过于复杂,因此无法在 API Gateway 中完成查询,必须使用单独的服务。

API 组合器应该使用响应式编程模型

在开发分布式系统时,我们一直努力降低服务之间的延迟。API 组合器应尽可能地并行调用提供方服务,最大限度地缩短查询操作的响应时间。例如,Find Order Aggregator 应该同时调用这四个服务,因为调用之间没有依赖关系。但有时,API 组合器需要一个提供方服务的结果才能调用另一个服务。在这种情况下,它需要按顺序调用一部分(但希望不是全部)提供方服务。

高效执行顺序和并行服务调用混合的逻辑可能很复杂。为了使 API 组合器达到较高的可维护性、性能和可扩展性,它应该使用基于 Java CompletableFuture、RxJava 可观测或其他类似的响应式设计。我将在后面讲 API Gateway 模式的时候再深入讨论这个主题。

API 组合模式的好处和弊端

此模式是在微服务架构中实现查询操作的简单直观方式。但它也有一些缺点:

增加了额外的开销。

带来可用性降低的风险。

缺乏事务数据一致性。

我们来逐一分析。

增加了额外的开销

这种模式的一个缺点是它需要调用多个服务和查询多个数据库,这带来了额外的开销。在单体应用程序中,客户端可以使用单个请求检索数据,这通常会执行单个数据库查询。相比之下,使用 API 组合模式会涉及多个请求和多个数据库查询。因此,它需要更多计算和网络资源,运行应用程序的成本也相应增加。

带来可用性降低的风险

这种模式的另一个缺点是导致可用性降低。如第 3 章所述,操作的可用性随着所涉及的服务数量而下降。因为查询操作的实现涉及至少三个服务:API 组合器和至少两个提供方服务,其可用性将显著小于单个服务的可用性。例如,如果单个服务的可用性为 99.5%,则调用四个提供方服务的 findOrder() 接口的可用性为 99.5%(4+1)=97.5% !

你可以使用几种策略来提高可用性。一种策略是 API 组合器在提供方服务不可用时,返回先前缓存的数据。API 组合器有时会缓存提供方服务返回的数据,以提高性能。它还可以使用此缓存来提高可用性。如果提供方服务不可用,则 API 组合器可以从缓存中返回数据,尽管这些缓存数据可能是过时的。

另一种提高可用性的策略是让 API 组合器返回不完整的数据。例如,假设KitchenService 暂时不可用。findOrder() 查询操作的 API 组合器可以从响应中省略该服务的数据,因为用户界面仍然可以显示其他有用的信息。你将在后面一章中看到有关 API 设计、缓存和可靠性的更多详细信息。

缺乏事务数据一致性

API 组合模式的另一个缺点是缺乏数据一致性。单体应用程序通常使用一个数据库事务执行查询操作。ACID 事务受制于隔离级别的约束,可以确保应用程序具有一致的数据视图,即使它执行多个数据库查询。相反,API 组合模式则是针对多个数据库执行查询。这种方式
存在一种风险,即查询操作将返回不一致的数据。

例如,从 Order Service 检索的 Order 可能处于 CANCELED 状态,而从 Kitchen Service 检索的相应 Ticket 可能尚未取消。API 组合器必须解决这种差异,因为这会增加代码复杂性。更糟糕的是,API组合器可能无法总是检测到不一致的数据,并将其返回给客户端。尽管存在这些缺点,API 组合模式还是非常有用的。你可以使用它来实现许多查询操作。但是有一些查询操作无法使用此模式有效实现。

以上内容摘自《微服务架构设计模式》一书。

文章来源:微信公众号 江南一点雨

上一篇:微服务需要拆分到什么程度?


下一篇:深度学习高能干货:手把手教你搭建MXNet框架