编者:本系列共7篇文章,现已全部发布:
- 微服务简介
- 构建微服务:使用API网关(本文)
- 构建微服务:微服务架构中的进程间通信
- 微服务架构中的服务发现
- 微服务的事件驱动型数据管理
- 选择微服务部署策略
- 将单体应用重构为微服务
还可以下载包含全套文章及使用NGINX Plus实施微服务相关信息的电子书:微服务:从设计到部署。此外,请查看新的微服务解决方案页面。
这套由7篇文章组成的系列文章中,第一篇文章介绍了微服务的设计、构建和部署,并讲解了微服务架构模式。文章探讨了使用微服务的优势和不足,以及为何微服务虽然复杂,但通常仍是复杂应用的理想选择。本文是该系列的第二篇,将介绍如何使用API网关构建微服务。
如果选择将应用构建为一组微服务,就需要确定应用客户端如何与这些微服务交互。单体应用只有一组端点(通常经过复制和负载均衡)。而在微服务架构中,每个微服务通常都会公开一组细粒度端点。本文将探讨这会如何影响客户端与应用之间的通信,并提出一种使用API 网关的方法。
简介
假设您正在为一款购物应用开发原生移动客户端。您可能需要实现一个商品详情页面,用于显示任意指定商品的信息。
例如,下图展示了在Amazon Android移动应用中滚动浏览商品详情时看到的内容。

尽管这是一款智能手机应用,但产品详情页面仍会显示大量信息。例如,除了基本的产品信息(如名称、描述和价格)外,此页面还会显示:
- 购物车中的商品数量
- 订单历史
- 客户评价
- 库存不足警告
- 配送选项
- 各种推荐,包括经常与此产品一起购买的其他产品、购买此产品的客户还购买的其他产品,以及购买此产品的客户还浏览过的其他产品
- 其他购买选项
使用单体应用架构时,移动客户端会通过向应用发出一次REST调用(GET api.company.com/productdetails/productId)来检索这些数据。负载均衡器会将请求路由到N个相同应用实例中的一个。随后,应用会查询多个数据库表,并将响应返回给客户端。
相比之下,采用微服务架构时,产品详情页面上显示的数据由多个微服务分别管理。以下是可能管理示例产品详情页面所显示数据的一些微服务:
- 购物车服务:购物车中的商品数量
- 订单服务:订单历史记录
- Catalog Service:基本产品信息,例如名称、图片和价格
- 评论服务:客户评论
- 库存服务:低库存警告
- 配送服务:从配送服务商的API中分别获取配送选项、截止时间和费用
- 推荐服务:推荐商品

我们需要确定移动客户端如何访问这些服务。下面来看看有哪些方案。
客户端与微服务直接通信
理论上,客户端可以直接向每个微服务发出请求。每个微服务都有一个公共端点(https://serviceName.api.company.name)。此URL将映射到微服务的负载均衡器,由负载均衡器将请求分发到可用实例。为获取产品详细信息,移动客户端需要向上述每项服务发出请求。
遗憾的是,此方案存在一些挑战和局限。其中一个问题是,客户端的需求与各微服务公开的细粒度API并不匹配。在本例中,客户端必须分别发出7个请求。在更复杂的应用中,可能需要发出更多请求。例如,Amazon曾介绍,其产品页面的呈现涉及数百项服务。客户端或许可以通过局域网发出如此多的请求,但通过公共互联网发出这些请求可能效率过低,而通过移动网络发出则肯定不可行。此外,这种方法还会使客户端代码复杂得多。
客户端直接调用微服务还存在另一个问题:某些微服务可能使用对Web不友好的协议。例如,一个服务可能使用Thrift二进制RPC,另一个服务则可能使用AMQP消息传递协议。这两种协议对浏览器和防火墙都不太友好,更适合在内部使用。应用程序在防火墙外部应使用HTTP和WebSocket等协议。
这种方法的另一个缺点是会增加微服务重构的难度。随着时间的推移,我们可能需要调整系统的服务划分方式。例如,可能需要合并两个服务,或将一个服务拆分为两个或更多服务。但是,如果客户端直接与这些服务通信,这类重构可能会变得极其困难。
由于存在这类问题,客户端通常不适合直接与微服务通信。
使用API网关
通常,更好的方法是使用所谓的API 网关。API 网关是系统的唯一入口服务器,类似于面向对象设计中的外观模式。API 网关封装了系统内部架构,并为每个客户端提供量身定制的API。它还可能承担其他职责,例如身份验证、监控、负载均衡、缓存、请求整形与管理,以及静态响应处理。
下图展示了API Gateway通常如何融入整体架构:

API 网关负责请求路由、组合和协议转换。来自客户端的所有请求都会先经过 API 网关,再由其路由到相应的微服务。API 网关通常会调用多个微服务并聚合结果,以处理请求。它可以在 HTTP 和 WebSocket 等 Web 协议与内部使用、不适用于 Web 的协议之间进行转换。
API Gateway还可以为每个客户端提供自定义API。它通常会向移动客户端公开粗粒度API。以产品详情场景为例,API Gateway可以提供一个端点(/productdetails?productid=xxx),让移动客户端只需发送一次请求,即可检索产品的所有详细信息。API Gateway通过调用产品信息、推荐、评论等多项服务并合并结果来处理该请求。
Netflix API Gateway是API Gateway的一个典型示例,即Netflix API Gateway。Netflix流媒体服务支持数百种不同类型的设备,包括电视、机顶盒、智能手机、游戏系统和平板电脑等。最初,Netflix曾尝试为其流媒体服务提供一个通用型API。然而,由于设备种类繁多且各有独特需求,他们发现这种方式效果不佳。如今,他们使用API Gateway,通过运行特定于设备的适配器代码,为每种设备提供量身定制的API。适配器通常会调用平均6至7个后端服务来处理每个请求。Netflix API Gateway每天处理数十亿个请求。
API网关的优势与不足
不难预见,使用API Gateway既有优势,也有不足。API Gateway的一项主要优势是能够封装应用的内部结构。客户端无需调用特定服务,只需与网关通信。API Gateway会为每类客户端提供专用API,从而减少客户端与应用之间的往返通信次数,并简化客户端代码。
API 网关也存在一些缺点。它是另一个必须开发、部署和管理的高可用组件。此外,API 网关还可能成为开发瓶颈。开发人员必须更新 API 网关,才能公开每个微服务的端点。因此,API 网关的更新流程应尽可能轻量。否则,开发人员将不得不排队等待更新网关。尽管存在这些缺点,但对于大多数实际应用而言,使用 API 网关仍是合理的选择。
实现API网关
了解了使用API网关的动因和权衡因素后,接下来看看需要考虑的各项设计问题。
性能与可扩展性
只有少数公司达到Netflix这样的运营规模,需要每天处理数十亿个请求。不过,对于大多数应用而言,API Gateway的性能和可扩展性通常都非常重要。因此,适合在支持异步非阻塞I/O的平台上构建API Gateway。实现可扩展API Gateway的技术有很多。在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 Gateway代码。响应式抽象的示例包括Scala中的Future、Java 8中的CompletableFuture以及JavaScript中的Promise。此外还有Reactive Extensions(也称为Rx或ReactiveX),最初由Microsoft为.NET平台开发。Netflix专门为其API Gateway创建了适用于JVM的RxJava。此外,JavaScript还有RxJS,可在浏览器和Node.js中运行。采用响应式方法,可以编写简洁而高效的API Gateway代码。
服务调用
基于微服务的应用是一个分布式系统,必须使用进程间通信机制。进程间通信分为两种模式。一种是基于消息的异步机制。部分实现使用JMS或AMQP等消息代理;另一些实现(如Zeromq)则不使用代理,由服务直接通信。另一种是HTTP或Thrift等同步机制。系统通常会同时使用异步和同步模式,甚至可能为每种模式采用多种实现。因此,API Gateway需要支持多种通信机制。
服务发现
API Gateway需要知道与其通信的每个微服务的位置(IP地址和端口)。在传统应用中,或许可以将这些位置硬编码,但在现代云端微服务应用中,这并非易事。消息代理等基础设施服务通常具有静态位置,可通过OS环境变量指定。然而,确定应用服务的位置并不容易。应用服务的位置是动态分配的。此外,由于自动扩缩容和升级,服务实例集合也会动态变化。因此,与系统中的其他服务客户端一样,API Gateway需要使用系统的服务发现机制,即服务端发现或客户端发现。后续文章将更详细地介绍服务发现。目前需要注意的是,如果系统使用客户端发现,API Gateway必须能够查询服务注册表,即存储所有微服务实例及其位置的数据库。
处理部分故障
实现API网关时,还必须解决部分故障问题。在所有分布式系统中,只要一个服务调用另一个响应缓慢或不可用的服务,就会出现此问题。API网关绝不能因等待下游服务而无限期阻塞。不过,具体如何处理故障取决于实际场景以及发生故障的服务。例如,在产品详情场景中,如果推荐服务无响应,API网关应向客户端返回其余产品详情,因为这些信息对用户仍然有用。推荐内容可以为空,也可以替换为预设的热门前十列表等内容。但是,如果产品信息服务无响应,API网关则应向客户端返回错误。
如果有可用的缓存数据,API Gateway也可以将其返回。例如,由于产品价格很少变动,当定价服务不可用时,API Gateway可以返回缓存的定价数据。数据可以由API Gateway自行缓存,也可以存储在Redis或Memcached等外部缓存中。通过返回默认数据或缓存数据,API Gateway可避免系统故障影响用户体验。
Netflix Hystrix 是一个非常实用的库,可用于编写调用远程服务的代码。对于超过指定阈值的调用,Hystrix 会使其超时。它实现了断路器模式,避免客户端无谓地等待无响应的服务。如果某项服务的错误率超过指定阈值,Hystrix 会触发断路器,使所有请求在指定时间段内立即失败。Hystrix 允许您定义请求失败时的回退操作,例如从缓存中读取数据或返回默认值。如果使用 JVM,建议考虑使用 Hystrix。如果在非 JVM 环境中运行,则应使用功能相当的库。
总结
对于大多数基于微服务的应用,实现API Gateway是合理的选择,它可作为系统的统一入口。API Gateway负责请求路由、组合和协议转换,并为应用的每个客户端提供定制API。API Gateway还可以通过返回缓存数据或默认数据来屏蔽后端服务故障。本系列的下一篇文章将介绍服务间通信。
编者:本系列共7篇文章,现已全部发布:
- 微服务简介
- 构建微服务:使用API网关(本文)
- 构建微服务:微服务架构中的进程间通信
- 微服务架构中的服务发现
- 微服务的事件驱动型数据管理
- 选择微服务部署策略
- 将单体应用重构为微服务
还可以下载包含全套文章及使用NGINX Plus实施微服务相关信息的电子书:《微服务:从设计到部署》。
如需详细了解其他用例,请参阅我们的三篇系列博客文章《将 NGINX Plus 部署为 API 网关》:
- 第 1 部分提供了多个用例的详细配置说明。
- 第 2 部分在这些用例的基础上进一步扩展,并介绍了可用于保护生产环境中后端 API 服务并确保其安全的一系列防护措施。
- 第 3 部分介绍如何将NGINX Plus部署为gRPC服务的API网关。
客座博主Chris Richardson是最初的CloudFoundry.com的创始人。该平台是早期面向Amazon EC2的Java平台即服务(PaaS)。目前,他为各组织提供咨询服务,帮助改进应用开发和部署方式。他还定期在http://microservices.io上撰写有关微服务的博客。
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
