博客 | NGINX

构建微服务: 使用 API 网关

NGINX-F5-horiz-black-type-RGB 的一部分
克里斯·理查森缩略图
克里斯·理查森
2015 年 6 月 15 日发布

编辑– 本系列文章共七部分,现已完成:

  1. 微服务简介
  2. 构建微服务: 使用 API 网关(本文)
  3. 构建微服务: 微服务架构中的进程间通信
  4. 微服务架构中的服务发现
  5. 微服务的事件驱动数据管理
  6. 选择微服务部署策略
  7. 将整体式架构重构为微服务

您还可以下载完整的文章集,以及有关使用 NGINX Plus 实现微服务的信息,作为电子书 -微服务: 从设计到部署。 另外,请查看新的微服务解决方案页面

关于设计、构建和部署微服务的七部分系列文章的第一篇介绍了微服务架构模式。 它讨论了使用微服务的优点和缺点,以及尽管微服务很复杂,但它们通常是复杂应用的理想选择。 这是本系列的第二篇文章,将讨论使用 API 网关构建微服务。

当您选择将应用构建为一组微服务时,您需要决定应用程序的客户端如何与微服务交互。 对于单片应用来说,只有一组(通常是复制的、负载平衡的)端点。 然而,在微服务架构中,每个微服务都会公开一组通常细粒度的端点。 在本文中,我们研究这对客户端到应用的通信有何影响,并提出了一种使用API 网关的方法。

介绍

假设您正在为购物应用开发本机移动客户端。 您可能需要实现一个产品详细信息页面,以显示有关任何给定产品的信息。

例如,下图显示了在亚马逊的 Android 移动应用中滚动浏览产品详细信息时您将看到的内容。

亚马逊 Android 移动应用程序的索引元素,如手机屏幕上显示的一样

尽管这是一个智能手机应用,但产品详细信息页面显示了很多信息。 例如,该页面不仅包含基本的产品信息(如名称、描述和价格),还显示:

  • 购物车中的商品数量
  • 订单历史记录
  • 消费者评论
  • 低库存警告
  • 送货选项
  • 各种推荐,包括经常与该产品一起购买的其他产品、购买该产品的顾客购买的其他产品以及购买该产品的顾客查看的其他产品
  • 其他购买选择

当使用单片应用架构时,移动客户端可以通过对应用进行单个 REST 调用 ( GET api.company.com/productdetails/productId ) 来检索此数据。 负载均衡器将请求路由到 N 个相同的应用实例之一。 然后,应用将查询各种数据库表并将响应返回给客户端。

相反,当使用微服务架构时,产品详细信息页面上显示的数据由多个微服务拥有。 以下是示例产品详细信息页面上显示的数据的一些潜在微服务:

  • 购物车服务——购物车中的商品数量
  • 订单服务——订单历史记录
  • 目录服务——基本产品信息,例如产品名称、图片和价格
  • 评论服务——客户评论
  • 库存服务 - 低库存警告
  • 配送服务 ——配送选项、截止期限和费用由配送提供商的 API 单独制定
  • 推荐服务 - 推荐商品

电子商务应用程序的移动客户端需要一种方式来访问 7 个微服务的 RESTful API

我们需要决定移动客户端如何访问这些服务。 让我们看看有哪些选项。

客户端与微服务直接通信

理论上,客户端可以直接向每个微服务发出请求。 每个微服务都有一个公共端点( https:// serviceName .api.company.name )。 该 URL 将映射到微服务的负载均衡器,该负载均衡器在可用实例之间分配请求。 为了检索产品详细信息,移动客户端将向上面列出的每个服务发出请求。

不幸的是,此选项存在挑战和局限性。 一个问题是客户端的需求与每个微服务公开的细粒度 API 不匹配。 本例中的客户端必须提出七个单独的请求。 在更复杂的应用中,可能必须进行更多操作。 例如,亚马逊描述了如何有数百种服务参与呈现其产品页面。 虽然客户端可以通过 LAN 发出那么多请求,但在公共互联网上这可能效率太低,而且在移动网络上肯定不切实际。 这种方法也使得客户端代码变得更加复杂。

客户端直接调用微服务的另一个问题是,有些客户端可能使用非网络友好的协议。 一项服务可能使用 Thrift 二进制 RPC,而另一项服务可能使用 AMQP 消息传递协议。 这两种协议都不是特别适合浏览器或防火墙,最好在内部使用。 应用应该在防火墙外使用 HTTP 和 WebSocket 等协议。

这种方法的另一个缺点是它使得重构微服务变得困难。 随着时间的推移,我们可能想要改变系统划分为服务的方式。 例如,我们可能会合并两项服务,或者将一项服务拆分为两项或多项服务。 然而,如果客户端直接与服务通信,那么执行这种重构就会非常困难。

由于这些问题,客户端直接与微服务对话几乎没有意义。

使用 API 网关

通常更好的方法是使用所谓的API 网关。 API 网关是系统的唯一入口点服务器。 它类似于面向对象设计中的外观 (Facade)模式。 API 网关封装了内部系统架构,并提供了针对每个客户端量身定制的 API。 它可能还有其他职责,例如身份验证、监控、负载均衡、缓存、请求整形和管理以及静态响应处理。

下图展示了 API 网关通常如何融入架构:

API 网关使电子商务应用程序的移动客户端能够访问其 7 个微服务的 RESTful API

API网关负责请求路由、组合和协议转换。 来自客户端的所有请求首先通过 API 网关。 然后它将请求路由到适当的微服务。 API 网关通常通过调用多个微服务并聚合结果来处理请求。 它可以在 HTTP 和 WebSocket 等 Web 协议与内部使用的非 Web 友好协议之间进行转换。

API 网关还可以为每个客户端提供自定义 API。它通常为移动客户端公开粗粒度的 API。 例如,考虑产品详细信息场景。 API 网关可以提供一个端点( /productdetails?productid= xxx ),使移动客户端能够通过单个请求检索所有产品详细信息。 API 网关通过调用各种服务(产品信息、推荐、评论等)并合并结果来处理请求。

API 网关的一个很好的例子是Netflix API 网关。 Netflix 流媒体服务可在数百种不同的设备上使用,包括电视、机顶盒、智能手机、游戏系统、平板电脑等。 最初,Netflix 尝试为其流媒体服务提供一刀切的API。 然而,他们发现,由于设备种类繁多且需求独特,这种方法效果并不好。 今天,他们使用 API 网关,通过运行特定于设备的适配器代码,为每台设备提供定制的 API。 适配器通常通过平均调用六到七个后端服务来处理每个请求。 Netflix API 网关每天处理数十亿个请求。

API 网关的优点和缺点

正如您所料,使用 API 网关既有优点也有缺点。 使用 API 网关的一个主要好处是它封装了应用的内部结构。 客户端无需调用特定的服务,只需与网关对话即可。 API 网关为每种客户端提供特定的 API。这减少了客户端和应用之间的往返次数。 它还简化了客户端代码。

API 网关也有一些缺点。 它是另一个必须开发、部署和管理的高可用性组件。 API 网关也有可能成为开发瓶颈。 开发人员必须更新 API 网关才能公开每个微服务的端点。 重要的是,更新 API 网关的过程应该尽可能轻量。 否则,开发人员将被迫排队等待更新网关。 尽管存在这些缺点,但对于大多数实际应用来说,使用 API 网关是有意义的。

实现 API 网关

现在我们已经了解了使用 API 网关的动机和权衡,让我们看看您需要考虑的各种设计问题。

性能和可扩展性

只有少数公司能达到 Netflix 的规模,并且每天需要处理数十亿个请求。 然而,对于大多数应用来说,API 网关的性能和可扩展性通常非常重要。 因此,在支持异步、非阻塞 I/O 的平台上构建 API 网关是有意义的。 有多种不同的技术可用于实现可扩展的 API 网关。 在 JVM 上,您可以使用基于 NIO 的框架之一,例如 Netty、Vertx、Spring Reactor 或 JBoss Undertow。 一种流行的非 JVM 选项是 Node.js,它是一个基于 Chrome 的 JavaScript 引擎构建的平台。 另一个选择是使用NGINX Plus 。 NGINX Plus 提供成熟、可扩展、高性能的 Web 服务器和反向代理,易于部署、配置和编程。 NGINX Plus 可以管理身份验证、访问控制、负载均衡请求、缓存响应,并提供应用感知的健康检查和监控。

使用反应式编程模型

API 网关通过简单地将某些请求路由到适当的后端服务来处理它们。 它通过调用多个后端服务并聚合结果来处理其他请求。 对于某些请求(例如产品详细信息请求),对后端服务的请求彼此独立。 为了最大限度地减少响应时间,API 网关应该同时执行独立请求。 但有时请求之间存在依赖关系。 在将请求路由到后端服务之前,API 网关可能首先需要通过调用身份验证服务来验证请求。 类似地,要获取有关客户愿望清单中的产品的信息,API 网关必须首先检索包含该信息的客户个人资料,然后检索每个产品的信息。 API 组合的另一个有趣的例子是Netflix Video Grid

使用传统的异步回调方法编写 API 组合代码很快就会导致回调地狱。 代码会变得混乱、难以理解,而且容易出错。 一种更好的方法是使用反应式方法以声明式风格编写 API 网关代码。 反应式抽象的示例包括 Scala 中的Future 、Java 8 中的CompletableFuture和 JavaScript 中的Promise 。 还有Reactive Extensions (也称为 Rx 或 ReactiveX),它最初由 Microsoft 为 .NET 平台开发。 Netflix 为 JVM 创建了 RxJava,专门用于其 API 网关。 还有适用于 JavaScript 的 RxJS,它可以在浏览器和 Node.js 中运行。 使用反应式方法将使您能够编写简单而高效的 API 网关代码。

服务调用

基于微服务的应用是一个分布式系统,必须使用进程间通信机制。 进程间通信有两种方式。 一种选择是使用异步的、基于消息传递的机制。 一些实现使用消息代理,例如 JMS 或 AMQP。 其他的,例如 Zeromq,是无代理的,并且服务之间直接通信。 另一种进程间通信方式是同步机制,例如 HTTP 或 Thrift。 系统通常同时使用异步和同步样式。 它甚至可能使用每种风格的多种实现。 因此,API 网关需要支持多种通信机制。

服务发现

API 网关需要知道与其通信的每个微服务的位置(IP 地址和端口)。 在传统应用中,您可能可以硬连线位置,但在现代基于云的微服务应用中,这是一个不小的问题。 基础设施服务(例如消息代理)通常具有静态位置,可以通过操作系统环境变量指定。 然而,确定应用服务的位置并不是那么容易。 应用服务具有动态分配的位置。 此外,由于自动扩展和升级,服务的实例集会动态变化。 因此,API 网关与系统中的任何其他服务客户端一样,需要使用系统的服务发现机制:服务器端发现客户端发现后续文章将更详细地描述服务发现。 目前,值得注意的是,如果系统使用客户端发现,那么 API 网关必须能够查询服务注册表,它是所有微服务实例及其位置的数据库。

处理部分故障

实现 API 网关时必须解决的另一个问题是部分失败的问题。 每当一个服务调用另一个响应缓慢或不可用的服务时,所有分布式系统都会出现此问题。 API 网关不应无限期地阻塞以等待下游服务。 但是,它如何处理故障取决于具体情况以及哪个服务出现故障。 例如,如果推荐服务在产品详细信息场景中没有响应,则 API 网关应将其余产品详细信息返回给客户端,因为它们对用户仍然有用。 这些建议可以是空的,也可以被诸如固定的前十名列表等所取代。 但是,如果产品信息服务没有响应,则 API Gateway 应该向客户端返回错误。

如果可用,API 网关还可以返回缓存数据。 例如,由于产品价格很少变动,如果定价服务不可用,API 网关可以返回缓存的定价数据。 数据可以由 API 网关本身缓存,也可以存储在外部缓存中,例如 Redis 或 Memcached。 通过返回默认数据或缓存数据,API 网关可确保系统故障不会影响用户体验。

Netflix Hystrix是一个非常有用的库,用于编写调用远程服务的代码。 Hystrix 会使超过指定阈值的调用超时。 它实现了一种断路器模式,可以阻止客户端不必要地等待无响应的服务。 如果服务的错误率超过指定的阈值,Hystrix 将触发断路器,并且所有请求将在指定的时间内立即失败。 Hystrix 允许您定义请求失败时的回退操作,例如从缓存中读取或返回默认值。 如果您正在使用 JVM,那么您绝对应该考虑使用 Hystrix。 并且,如果您在非 JVM 环境中运行,则应该使用等效库。

概括

对于大多数基于微服务的应用来说,实现 API 网关是有意义的,它充当系统的单一入口点。 API网关负责请求路由、组合和协议转换。 它为每个应用程序的客户端提供自定义 API。API 网关还可以通过返回缓存或默认数据来掩盖后端服务中的故障。 在本系列的下一篇文章中,我们将讨论服务之间的通信。

编辑– 本系列文章共七部分,现已完成:

  1. 微服务简介
  2. 构建微服务: 使用 API 网关(本文)
  3. 构建微服务: 微服务架构中的进程间通信
  4. 微服务架构中的服务发现
  5. 微服务的事件驱动数据管理
  6. 选择微服务部署策略
  7. 将整体式架构重构为微服务

您还可以下载完整的文章集,以及有关使用 NGINX Plus 实现微服务的信息,作为电子书 -微服务: 从设计到部署

有关其他用例的详细信息,请参阅我们的三部分博客系列《将 NGINX Plus 部署为 API 网关》

  • 第 1 部分提供了几种用例的详细配置说明。
  • 第 2 部分扩展了这些用例,并研究了可用于保护和确保生产中的后端 API 服务的一系列保护措施。
  • 第 3 部分解释如何将 NGINX Plus 部署为 gRPC 服务的 API 网关。

客座博主 Chris Richardson 是原CloudFoundry.com的创始人,该网站是针对 Amazon EC2 的早期 Java PaaS(平台即服务)。 他现在为各组织提供咨询服务,以改进他们开发和部署应用的方式。 他还定期在http://microservices.io上撰写有关微服务的博客。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”