这是本系列的第二篇文章,将讨论使用API网关构建微服务。当选择将应用程序构建为一组微服务时,需要确定应用程序的客户端如何与微服务进行交互。
在本文中,我们将研究它如何影响客户端到应用程序的通信,并提出一种使用API网关的方法。
让我们假设正在为购物应用程序开发原生移动客户端,需要实现商品详细信息页面,该页面显示有关给定产品的所有信息。
例如,下图显示了在亚马逊Android移动应用程序中滚动浏览商品详细信息时看到的内容。
即使这是智能手机应用程序,商品详细信息页面也会显示很多信息。例如,不仅有基本的产品信息(如名称,描述和价格),而且此页面还显示:
使用单体应用架构时,移动客户端将通过单个REST调用来检索此数据(GET api.company.com/productdetails/productId
)到应用程序。负载均衡器将请求路由到N个相同的应用程序实例之一。然后,应用程序将查询各种数据库表并将响应返回给客户端。
当使用微服务架构时,商品详细信息页面上显示的数据由多个微服务拥有。以下是一些潜在的微服务,它们拥有示例商品详细信息页面上显示的数据:
我们需要决定移动客户端如何访问这些服务。
理论上,客户端可以直接向每个微服务发出请求。每个微服务都有一个公共端点(https://serviceName.api.company.name
)。此URL将映射到微服务的负载均衡器,该负载均衡器跨可用实例分发请求。要检索商品详细信息,移动客户端将向上述每个微服务发出请求。
不幸的是,这种方式存在挑战和局限:
客户端的需求与每个微服务公开的细粒度API之间的不匹配。
此示例中的客户端必须发出七个单独的请求,在更复杂的应用程序中,它可能会发出更多的请求(例如,亚马逊描述了在渲染产品页面时如何涉及数百种服务)。虽然客户端可以通过局域网发出这么多请求,但是这在公共互联网上效率太低,而且在移动网络上肯定是行不通的。这种方法也使客户端代码更加复杂。
可能会使用非Web友好的协议:
这两种协议都不适合浏览器或防火墙,最好在内部网络中使用,应用程序应在防火墙之外使用HTTP和WebSocket等协议。
重构微服务变得困难。随着时间的推移,我们可能想要修改系统拆分为微服务的划分方式,重新定义微服务模块的边界。
例如,我们可能合并两个服务或将服务拆分为两个或更多服务。但是,如果客户端直接与服务通信,那么执行这种重构可能非常困难。
由于这些问题,客户直接与微服务通信变的没有意义。
通常,更好的方法是使用API网关。API网关是进入系统的单一入口点服务。它类似于面向对象设计的外观模式(Facade Pattern)。API网关封装了内部系统体系结构,并提供了为每个客户端量身定制的API。它可能还有其他职责,例如:
下图显示了API网关通常如何适合该体系结构:
API网关负责请求路由,组合和协议转换。
客户的所有请求首先通过API网关。然后它将请求路由到适当的微服务。API网关通常通过调用多个微服务并聚合结果来处理请求。它可以在Web协议(如HTTP和WebSocket)和内部使用的Web友好协议之间进行转换。
API网关为每个客户端提供自定义API,通常为移动客户端公开粗粒度API。
例如,展示商品详细信息的方案。API网关可以提供端点(/productdetails?productid=xxx
),使端点能够通过单个请求检索所有商品详细信息。API网关通过调用各种服务(目录服务,推荐服务,审核服务等)来处理请求,并将结果组合在一起。
API网关示例Netflix API
网关。
Netflix流媒体服务可用于数百种不同类型的设备,包括电视,机顶盒,智能手机,游戏系统,平板电脑等。最初,Netflix试图为其流媒体服务提供一种通用的API。然而,他们发现由于各种设备和它们的独特需求,通用API不能很好地工作。现在,他们使用API网关,通过运行特定设备的适配器代码为每个设备提供量身定制的API。通常,适配器平均调用六到七个后端服务来处理每一个请求。Netflix API Gateway每天处理数十亿个请求。
客户端只需与网关通信,而不必调用特定服务。API网关为每种客户端提供特定的API。这减少了客户端和应用程序之间的往返次数,还简化了客户端代码。
缺点:API网关是另一个必须开发,部署和管理的高可用组件。
API网关也存在开发瓶颈的风险。开发人员必须更新API网关才能公开每个微服务的端点。重要的是,更新API网关的过程尽可能轻量级。否则,开发人员将被迫排队等待更新网关。尽管存在这些缺点,但对于大多数应用来说,使用API网关是很有意义的。
现在我们已经了解了使用API网关的动机和权衡,让我们来看看需要考虑的各种设计问题。
虽然只有少数公司能够得到Netflix的运营规模,每天需要处理数十亿的请求。但是,对于大多数应用程序,API网关的性能和可伸缩性是非常重要。因此,需要在支持异步,非阻塞I/O的平台上构建API网关。
有多种不同的技术可用于实现可扩展的API网关:
另一种选择是使用NGINX Plus
NGINX Plus提供了一个成熟,可扩展,高性能的Web服务器和反向代理,可轻松部署,配置和编程。NGINX Plus可以管理身份验证,访问控制,负载平衡请求,缓存响应,并提供应用程序感知的运行状况检查和监视。
API网关通过简单地将它们路由到适当的后端服务来处理某些请求。它通过调用多个后端服务并聚合结果来处理其他请求。
有时请求之间存在依赖关系。在将请求路由到后端服务之前,API网关可能首先需要通过调用身份验证服务来验证请求,例如:
另一个有趣的API组合示例是Netflix Video Grid
。
使用传统的异步回调方法编写API组合代码很快就会导致回调地狱。代码将纠结、难以理解,并且容易出错。更好的方法是以声明式风格使用反应式编程模型编写API网关代码。反应式编程的抽象概念示例,包括:
使用反应式编程模型将能够编写简单而有效的API网关代码。
基于微服务的应用程序是一个分布式系统,必须使用进程间通信机制。进程间通信有两种类型:
API网关需要知道与之通信的每个微服务的位置(IP地址和端口):
基础结构服务(例如,消息代理)通常具有静态位置,可以通过OS环境变量指定。但是,确定应用程序服务的位置并不容易,应用服务具有动态分配的位置。由于自动扩展和升级,服务实例集会动态更改。因此,与网络中的任何其他服务客户端一样,API网关需要使用系统的服务发现机制:服务器端发现或客户端发现。
稍后的文章将更详细地描述服务发现。现在,值得注意的是,如果系统使用客户端发现,那么API网关必须能够查询服务注册表(Service Registry),它是所有微服务实例及其位置的数据库。
实现API网关时必须解决的另一个问题是部分失败的问题。一个服务调用另一个响应缓慢或不可用的服务,所有分布式系统都会出现此问题。API网关永远不应无限期地阻塞等待下游服务,如何处理故障取决于具体方案和哪个服务失败。
如果目录服务没有响应,那么API网关应该向客户端返回错误,或者,API网关也可以返回缓存的数据(例如,由于商品价格不会经常变化,API网关可以返回缓存的定价数据)。
数据可以由API网关本身缓存,也可以存储在外部缓存中,例如Redis或Memcached。通过返回默认数据或缓存数据,API网关可确保系统故障不会影响用户体验。
Netflix Hystrix
是一个非常有用的库,用于编写调用远程服务的代码。
Hystrix的超时调用次数可以设定阈值,它实现了断路器模式,阻止客户端不必要地等待无响应的服务。
对于大多数基于微服务的应用程序,实现API网关是有意义的,它充当系统的单一入口点。 API网关负责请求路由,组合和协议转换,它为每个应用程序的客户端提供自定义API。API网关还可以通过返回缓存或默认数据来屏蔽后端服务中的故障。在本系列的下一篇文章中,我们将介绍服务之间的通信。