使用微服务架构开发应用后,每个微服务都将拥有自己的API,设计应用外部API的任务因客户端的多样性而变得更具有挑战性。不同客户端通常需要不同的数据。通常基于PC浏览器的用户界面显示的信息要远多于移动设备的用户界面。此外,不同的客户端通过不同类型的网络访问服务。防火墙内的客户端使用高性能局域网,防火墙外的客户端使用性能较低的互联网或移动网络。所以,拥有单一、适合所有客户端的API通常没有意义。
API的一种设计思路是让客户端直接调用服务。在该设计中,客户端充当API组合器的角色,它调用多个服务并组合结果。从表面上看,这听起来很简单,就是客户端直接调用各微服务的API方法。但由于存在以下弊端,这种方法很少应用于微服务架构:
(1) 多次客户端请求导致用户体验不佳。细粒度服务API要求客户端发出多个请求以检索所需的数据,这样做效率太低,并且可能导致糟糕的用户体验。客户端和服务器之间的频繁交互可能导致应用看起来无响应,由其是当使用互联网或移动网络时。与局域网相比,互联网具有更低的带宽和更高的延迟,移动网络甚至更糟。移动网络和互联网的延迟通常是局域网的100倍。当多个请求必须顺序执行的时候,这将导致糟糕的用户体验。更重要的是,由于网络延迟导致的糟糕的用户体验并不是API需要考虑的唯一问题。它可能要求客户端开发人员编写复杂的API组合代码。而客户端开发人员首要任务是创建优质的用户体验,而不是分散精力与后端的API较劲。此外,由于每个网络请求都会消耗电力,因此多次API请求会更快地耗尽设备的电量,由其是移动设备。
(2) 缺乏封装导致前端开发的代码修改影响后端。由于客户端了解每项服务以及服务的API,从而导致封装不足(紧耦合),会对后续的架构调整或API调整带来阻碍。随着应用的发展,后端开发人员有时需要以破坏现有客户端的方式更改API,有时甚至进行服务拆分或合并。但是,如果将有关服务的知识融入到客户端中(导致客户端和服务器的过度耦合),则可能很难更改服务的API。
(3) 微服务可能使用对客户端而言不友好的进程间通信机制。客户端直接服务的另一个挑战是某些服务可能使用对客户端不友好的协议,由其是防火墙外的客户端。在防火墙的外部运行的客户端通常使用HTTPS等协议,但是,服务器端有许多可选协议,如gRPC协议、AMQP等消息协议等,这些协议在局域网内部运行良好,但可能不容易被客户端使用。
替换直接访问服务的设计的更好的方法是使用API Gateway。API Gateway是一种服务,它是外部世界进入微服务应用的入口点。它负责请求路由、API组合和身份认证等功能。
API Gateway概述
API Gateway 是一种服务,作为从防火墙外部进入微服务应用的唯一入口点。它类似于面向对象设计中的外观(Facade)模式。API Gateway 封装了应用的内部架构,并为其客户端提供API。它还可能具有其他职责,如身份认证、流量监控和速率限制。
客户端、API Gateway和微服务三者的关系如下:
API Gateway 负责请求路由、API组合和协议转换。来自外部客户端的所有API请求首先路由到API Gateway,后者将一些请求路由到相应的服务。API Gateway 使用 API 组合模式处理其他请求,调用多个服务并聚合结果。在笔者参与的云服务开发中,仅使用到API Gateway的路由功能。API组合能力和协议转换能力均通过微服务方式实现。
请求路由
API Gateway 的关键能力之一是请求路由。 API Gateway 通过将请求路由到相应的服务来实现一些API操作。当它收到请求时,API Gateway 会查询路由映射,该映射指定将请求路由到哪个服务。该功能与nginx等Web服务器提供的反向代理功能相同。
API组合
API Gateway 通常不仅仅是简单地扮演反向代理的角色。它也可能使用API组合实现一些API操作。如客户端发出一个请求,API Gateway从多个服务请求数据并组合后返回给客户端。
协议转换
API Gateway 也可以完成协议转换。它可能为外部客户端提供RESTful API,即使应用在内部使用混合协议,包括REST和gRPC等。在需要时,某些API的操作实现在RESTful 外部API和基于内部的gRPC API之间进行转换。
实现边缘功能
虽然 API Gateway 的主要职责是API路由和API组合,但它也可以实现所谓的边缘功能。边缘功能(Edge Function),是在应用边缘实现的请求、处理功能。应用可能实现的边缘功能包括:
(1) 身份认证: 验证发出请求的客户端身份。
(2) 访问授权: 验证客户端是否有权执行该特定操作。
(3) 速率限制: 限制特定客户或所有客户端每秒的请求数。
(4) 缓存:缓存响应以减少对服务的请求数。
(5) 指标收集: 收集有关API使用情况的指标,以进行计费分析。
(6) 请求日志: 记录请求历史。
在 API Gateway 中实现这些边缘功能(由其是访问授权)通常就很方便。网络跳跃少一个,就可改善延迟状况。需要改动的部分也较少,这就降低了复杂性。
API Gateway 架构概述
API Gateway 具有分层的模块化架构。其架构由两层构成:API层和公共层,示意图如下:
API层由一个或多个独立的API模块组成。每个API模块都为特定客户端实现API。公共层实现共享功能,包括边缘功能,如身份认证。
API Gateway 运维
明确 API Gateway 职责和架构后,接下来需要关心的就是 API Gateway 的运维。微服务架构下,提倡松散耦合的自治团队。为每个客户端提供一个API Gateway,即所谓的后端前置(Backend For Frontend, BFF)模式,由Phil Calcado和他的同事Sound Cloud开创。如图所示:
可以看到,每个API模块都成为自己的独立API Gateway,由对应的客户端团队运维。理论上,可以使用不同的技术栈开发不同的API Gateway。但这也可能通过复制代码开实现公共层的功能,如实现边缘功能的代码。
API Gateway的优缺点
接下来将介绍API Gateway的优缺点。
API Gateway 优点
使用 API Gateway 的一个主要好处是它封装了应用的内部结构。客户端不必调用特定服务,而是与API Gateway 通信。API Gateway 为每个客户端提供特定于客户端的API,从而减少客户端和应用之间的往返次数,它还简化了客户端代码。
API Gateway 缺点
API Gateway 的弊端是:它是一个必须部署和管理的高可用组件。开发人员必须更新API Gateway才能对外公开服务的API。更新API Gateway的过程尽可能轻量化是非常重要的。尽管存在上述弊端,但对于大多数应用来说,使用 API Gateway是有意义的。现有API Gateway 组件已经发展出模糊匹配的能力,这样,同一个微服务的接口只需进行一次基础路由的配置即可。
API Gateway 的设计难题
设计 API Gateway时,需要考虑以下问题:
(1) 性能和可扩展性。API Gateway是应用的入口。所有外部请求必须首先通过API Gateway。虽然大多数公司的运营规模没有每天处理上亿次请求,但 API Gateway 的性能和可扩展性通常非常重要。影响性能和可扩展性的关键设计决策时API Gateway应该使用同步还是异步I/O。
(2) 使用响应式编程抽象编写可维护的代码。API组合包括调用多个后端服务。一种方法是API端点处理程序方法按照依赖性确定的顺序调用服务。按照顺序调用服务的弊端是服务响应时间过长(响应时间是每个服务响应时间的总和)。为了最小化响应时间,组合逻辑应仅可能的同时调用多个服务。但,编写可维护的并发代码存在挑战。因为编写可扩展的并发代码的传统方法是使用回调。异步和事件驱动I/O本质上是基于回调的。使用传统的异步回调方法编写API组合代码很快会导致"回调地狱"。代码将纠结成一团,难以理解,且容易出错,尤其是当组合同时使用并发请求和顺序请求时。更好的方法是使用响应式方法,以声明式风格编写API组合代码。
(3) 处理局部故障。除了可扩展性外,API Gateway 也必须保证可靠性。实现可靠性的一种方法是在负载均衡器后面运行多个 API Gateway实例。如果一个实例失败,负载均衡器会将请求路由到其他实例。
参考
微服务架构设计模式 Chris Richardson 著, 陈斌 等 译