在微服务体系结构中,每个微服务通常都会公开一组精细终结点。 此事实可能会影响客户端到微服务的通信,如本部分所述。
客户端与微服务的直接通信
一种可能的方法是使用直接客户端到微服务的通信体系结构。 在此方法中,客户端应用可以直接向某些微服务发出请求,如图 4-12 所示。
图 4-12. 使用客户端直接与微服务通信的体系结构
在此方法中,每个微服务都有一个公共终结点,有时为每个微服务使用不同的 TCP 端口。 特定服务的 URL 示例可能是 Azure 中的以下 URL:
http://eshoponcontainers.westus.cloudapp.azure.com:88/
在基于群集的生产环境中,该 URL 将映射到群集中使用的负载均衡器,后者反过来又在微服务之间分配请求。 在生产环境中,可以在微服务和 Internet 之间拥有应用程序传送控制器(ADC),例如 Azure 应用程序网关 。 此层充当一个透明层,不仅执行负载均衡,而且通过提供 SSL 终止来保护服务。 此方法通过将 CPU 密集型 SSL 终止和其他路由职责卸载到 Azure 应用程序网关来提高主机的负载。 在任何情况下,负载均衡器和 ADC 都是从逻辑应用程序体系结构的角度来看透明的。
直接客户端到微服务通信体系结构对于基于微服务的小型应用程序可能足够好,尤其是在客户端应用是服务器端 Web 应用程序(如 ASP.NET MVC 应用)时。 但是,当你生成大型和复杂的基于微服务的应用程序(例如,处理数十种微服务类型时),尤其是当客户端应用是远程移动应用或 SPA Web 应用程序时,这种方法将面临一些问题。
根据微服务开发大型应用程序时,请考虑以下问题:
- 客户端应用如何最大程度地减少对后端的请求数,并减少与多个微服务的聊天通信?
与多个微服务交互以生成单个 UI 屏幕会增加 Internet 往返次数。 此方法会增加 UI 端的延迟和复杂性。 理想情况下,应在服务器端有效地聚合响应。 此方法可减少延迟,因为多个数据并行返回,并且某些 UI 可以在数据准备就绪后立即显示数据。
- 如何处理交叉问题,例如授权、数据转换和动态请求调度?
在每个微服务上实现安全性和跨领域关注点(例如安全性和授权)可能需要大量开发工作。 一种可能的方法是让 Docker 主机或内部群集中的这些服务限制从外部直接访问这些服务,并在集中位置(例如 API 网关)实现这些交叉关注点。
- 客户端应用如何与使用非 Internet 友好协议的服务通信?
客户端应用不支持服务器端使用的协议(如 AMQP 或二进制协议)。 因此,请求必须通过 HTTP/HTTPS 等协议执行,之后转换为其他协议。 这种情况下, 中间人 方法可以帮助。
- 如何塑造专为移动应用打造的外观?
多个微服务的 API 可能不适合满足不同客户端应用程序的需求。 例如,移动应用的需求可能与 Web 应用的需求不同。 对于移动应用,可能需要进一步优化,以便数据响应更高效。 可以通过聚合来自多个微服务的数据并返回一个统一的数据集,有时还需删除移动应用不需要的响应数据来实现这一功能。 当然,你可以压缩这些数据。 同样,在移动应用程序和微服务之间使用一个中介层或 API 会使这个场景更加便利。
为什么考虑 API 网关而不是直接客户端到微服务的通信
在微服务体系结构中,客户端应用通常需要使用多个微服务的功能。 如果这种操作是直接进行的,客户端需要处理对微服务接口的多个调用。 当应用程序发展并引入新微服务或更新现有微服务时,会发生什么情况? 如果您的应用程序有多个微服务,那么处理客户端应用如此多的端点可能会成为一场噩梦。 由于客户端应用将耦合到这些内部终结点,因此在将来演变微服务可能会对客户端应用产生高影响。
因此,对于基于微服务的应用程序,具有中间级别或间接层(网关)可能很方便。 如果没有 API 网关,客户端应用必须将请求直接发送到微服务,并引发问题,例如以下问题:
耦合:如果没有 API 网关模式,客户端应用将耦合到内部微服务。 客户端应用需要知道如何在微服务中分解应用程序的多个区域。 在演变和重构内部微服务时,由于客户端应用直接引用内部微服务,这些操作会影响维护,导致客户端应用出现重大变更。 客户端应用需要频繁更新,使解决方案更难发展。
往返次数过多:客户端应用中的单页/屏幕可能需要多次调用多个服务。 此方法可能会导致客户端和服务器之间的多个网络往返,从而增加大量的延迟。 在中间级别处理的聚合可以提高客户端应用的性能和用户体验。
安全问题:如果没有网关,所有微服务都必须向“外部世界”公开,使攻击面大于隐藏客户端应用不直接使用的内部微服务。 攻击面越小,应用程序就越安全。
跨领域问题:每个公开发布的微服务都必须处理授权和 SSL 等问题。 在许多情况下,这些问题可以在单个层中处理,以便简化内部微服务。
什么是 API 网关模式?
使用多个客户端应用程序设计和生成基于微服务的大型或复杂的应用程序时,可以考虑一种良好的方法是 API 网关。 此模式是为某些微服务组提供单一入口点的服务。 它类似于面向对象的设计 中的 Facade 模式 ,但在本例中,它是分布式系统的一部分。 API 网关模式有时也称为“为前端而设的后端”(BFF),因为它的设计是为了考虑客户端应用的需求。
因此,API 网关位于客户端应用和微服务之间。 它充当反向代理,将来自客户端的请求路由到服务。 它还可以提供其他交叉功能,例如身份验证、SSL 终止和缓存。
图 4-13 显示了自定义 API 网关如何能够融入一个仅包含少数微服务的简化微服务架构中。
图 4-13. 使用作为自定义服务实现的 API 网关
应用连接到单个终结点(API 网关),该网关配置为将请求转发到单个微服务。 在此示例中,API 网关将作为作为容器运行的自定义 ASP.NET Core WebHost 服务实现。
请务必强调,在该关系图中,你将使用面向多个和不同客户端应用的单个自定义 API 网关服务。 这一事实可能是一个重要的风险,因为 API 网关服务将基于客户端应用的许多不同要求进行增长和演变。 最终,由于这些不同的需求,它将膨胀,实际上它可能与整体应用程序或整体服务类似。 因此,强烈建议将 API 网关拆分为多个服务或多个较小的 API 网关,例如,每个客户端应用外形规格类型一个。
实现 API 网关模式时需要小心。 通常,让单个 API 网关聚合应用程序的所有内部微服务并不好。 如果这样做,它将充当整体聚合器或业务流程协调程序,并通过耦合所有微服务来违反微服务自治。
因此,API 网关应基于业务边界和客户端应用进行隔离,而不是充当所有内部微服务的单个聚合器。
将 API 网关层拆分为多个 API 网关时,如果应用程序具有多个客户端应用,这可以作为标识多个 API 网关类型的一个主要基点,以便根据每个客户端应用的需求实现不同的接口。 这种情况是名为“前端后端”(BFF)的模式,其中每个 API 网关都可以为每个客户端应用类型提供不同的 API,甚至可能基于客户端外形规格实现调用多个内部微服务下的特定适配器代码,如下图所示:
图 4-13.1. 使用多个自定义 API 网关
图 4-13.1 显示了按客户端类型隔离的 API 网关;一个用于移动客户端,一个用于 Web 客户端。 传统的 Web 应用连接到使用 Web API 网关的 MVC 微服务。 此示例描述了具有多个精细 API 网关的简化体系结构。 在这种情况下,为每个 API 网关标识的边界仅基于“前端后端”(BFF)模式,因此仅基于每个客户端应用所需的 API。 但在较大的应用程序中,还应根据业务边界进一步创建其他 API 网关,作为设计的第二个考虑因素。
API 网关模式中的主要功能
API 网关可以提供多个功能。 但是,根据产品,它可能提供更丰富或更简单的功能,任何 API 网关最重要的基础功能都是以下设计模式:
反向代理或网关路由。 API 网关提供反向代理,用于将请求(第 7 层路由(通常是 HTTP 请求)重定向或路由到内部微服务的终结点。 网关为客户端应用提供单个终结点或 URL,然后在内部将请求映射到一组内部微服务。 此路由功能有助于将客户端应用与微服务解耦,并且在通过将 API 网关置于整体 API 和客户端应用之间以实现现代化时也很便利。这样,你可以在添加新的 API 作为新微服务的同时,仍然使用旧式整体 API,直到未来将其拆分为多个微服务。 由于 API 网关,客户端应用不会注意到正在使用的 API 是作为内部微服务或整体 API 实现的,更重要的是,在将整体 API 演变和重构为微服务时,由于 API 网关路由,客户端应用不会受到任何 URI 更改的影响。
有关详细信息,请参阅 网关路由模式。
请求聚合。 作为网关模式的一部分,可以将多个客户端请求(通常是 HTTP 请求)聚合到单个客户端请求中,以多个内部微服务为目标。 当客户端页面/屏幕需要来自多个微服务的信息时,此模式尤其方便。 使用此方法,客户端应用向 API 网关发送单个请求,向内部微服务调度多个请求,然后聚合结果并将所有内容发送回客户端应用。 此设计模式的主要优点和目标是减少客户端应用与后端 API 之间的聊天性,这对于微服务所在的数据中心内的远程应用尤其重要,例如移动应用或来自客户端远程浏览器中 JavaScript 的 SPA 应用的请求。 对于在服务器环境中执行请求的常规 Web 应用(如 ASP.NET Core MVC Web 应用),此模式并不那么重要,因为延迟比远程客户端应用小得多。
根据所使用的 API 网关产品,它可能能够执行此聚合。 但是,在许多情况下,在 API 网关范围内创建聚合微服务更为灵活,因此可以在代码(即 C# 代码)中定义聚合:
有关详细信息,请参阅 网关聚合模式。
跨领域问题或网关卸载。 根据每个 API 网关产品提供的功能,可以将功能从单个微服务卸载到网关,从而通过将交叉关注点合并到一个层来简化每个微服务的实现。 此方法尤其适用于在每个内部微服务中正确实现的复杂专用功能,例如以下功能:
- 身份验证和授权
- 服务发现集成
- 响应缓存
- 重试策略、断路器和 QoS
- 速率限制和遏制
- 负载均衡
- 日志记录、跟踪、关联
- 标头、查询字符串和声明转换
- IP 允许列表
有关详细信息,请参阅 网关卸载模式。
将产品与 API 网关功能配合使用
根据每个实现,API 网关产品可能会提供更多跨领域问题。 我们将在此处探索:
Azure API 管理
Azure API 管理 (如图 4-14 所示)不仅解决了 API 网关的需求,而且还提供从 API 收集见解等功能。 如果使用 API 管理解决方案,则 API 网关只是该完整 API 管理解决方案中的组件。
图 4-14. 对 API 网关使用 Azure API 管理
Azure API 管理可同时满足 API 网关和管理需求,例如日志记录、安全性、计量等。在这种情况下,使用 Azure API 管理等产品时,可能具有单个 API 网关并不那么有风险,因为这些 API 网关是“更薄的”,这意味着你不会实现可能演变成整体组件的自定义 C# 代码。
API 网关产品通常表现为用于入口通信的反向代理,也可以从内部微服务筛选 API,并授权此单层中的已发布 API。
API 管理系统中提供的见解可帮助你了解 API 的使用方式及其执行方式。 他们通过让你查看准实时分析报告并确定可能影响业务的趋势来执行此作。 此外,还可以获取有关请求和响应活动的日志,以便进一步进行联机和脱机分析。
使用 Azure API 管理,可以使用密钥、令牌和 IP 筛选来保护 API。 借助这些功能,可以强制实施灵活精细的配额和速率限制、使用策略修改 API 的形状和行为,并通过响应缓存提高性能。
在本指南和参考示例应用程序(eShopOnContainers)中,体系结构仅限于更简单、自定义的容器化体系结构,以便在不使用 Azure API 管理等 PaaS 产品的情况下专注于普通容器。 但是,对于部署到 Microsoft Azure 的基于微服务的大型应用程序,我们建议将 Azure API 管理作为生产环境中的 API 网关的基础进行评估。
豹猫
Ocelot 是一种轻型 API 网关,建议采用更简单的方法。 Ocelot 是基于开源 .NET Core 的 API 网关,专为需要统一入口点的微服务体系结构创建。 它是轻量级、快速且可缩放的,在许多其他功能中提供路由和身份验证。
为 eShopOnContainers 参考应用程序 2.0 选择 Ocelot 的主要原因是 Ocelot 是一个 .NET Core 轻型 API 网关,可以在部署微服务/容器(例如 Docker 主机、Kubernetes 等)的同一应用程序部署环境中部署。由于它基于 .NET Core,因此跨平台允许你在 Linux 或 Windows 上部署。
上图显示了在容器中运行的自定义 API 网关,这正是在容器和基于微服务的应用程序中运行 Ocelot 的方式。
此外,市场上还有其他许多产品提供 API 网关功能,例如 Apigee、Kong、MuleSoft、WSO2 和其他产品,例如 Linkerd 和 Istio,用于服务网格入口控制器功能。
在初始体系结构和模式说明部分之后,后续部分介绍如何使用 Ocelot 实现 API 网关。
API 网关模式的缺点
最重要的缺点是,实现 API 网关时,会将该层与内部微服务耦合。 这种耦合可能会给应用程序带来严重的困难。 Azure 服务总线团队的架构师Clemens Vaster在 GOTO 2016 的“消息传递和微服务”会话中将这种潜在的困难称为“新的 ESB”。
使用微服务 API 网关可创建额外的单一故障点。
由于额外的网络调用,API 网关可能会引入更高的响应时间。 但是,相较于经常直接调用内部微服务的客户端接口,这种额外调用的影响更小。
如果未正确扩展,API 网关可能成为瓶颈。
如果 API 网关包含自定义逻辑和数据聚合,则需要额外的开发成本和将来维护。 开发人员必须更新 API 网关才能公开每个微服务的终结点。 此外,内部微服务中的实现更改可能会导致 API 网关级别的代码更改。 但是,如果 API 网关只是应用安全、日志记录和版本控制(就像使用 Azure API 管理时一样),则这种额外的开发成本可能不适用。
如果 API 网关由单个团队开发,则可能存在开发瓶颈。 多个细颗粒度的 API 网关可以适应不同客户端的需求,这是采用更好的方法的另一个原因。 还可以将 API 网关在内部隔离为多个区域或层,这些区域或层由处理内部微服务的不同团队拥有。
其他资源
克里斯·理查森 Pattern: API Gateway / Backend for Front-End(模式:API 网关/用于前端的后端)
https://microservices.io/patterns/apigateway.htmlAPI 网关模式
https://learn.microsoft.com/azure/architecture/microservices/gateway聚合和组合模式
https://microservices.io/patterns/data/api-composition.htmlAzure API 管理
https://azure.microsoft.com/services/api-management/乌迪·达汉 面向服务的组合
https://udidahan.com/2014/07/30/service-oriented-composition-with-video/克莱门斯·瓦瑟斯 GOTO 2016 中的消息传递和微服务(视频)
https://www.youtube.com/watch?v=rXi5CLjIQ9k简言之,API 网关 (ASP.NET 核心 API 网关教程系列)
https://www.pogsdotnet.com/2018/08/api-gateway-in-nutshell.html