在微服务体系结构中,每个微服务公开一组(通常)细粒度端点。如本节所述,这一事实可能会影响客户端到微服务的通信。
直接客户端到微服务通信
一种可能的方法是使用直接的客户机到微服务通信体系结构。在这种方法中,客户端应用程序可以直接向某些微服务发出请求,如图12所示。
图12 使用直接客户端到微服务的通信体系结
在这种方法中,每个微服务都有一个公开端点,有时每个微服务有一个不同的TCP端口。特定服务的URL示例可以是Azure中的以下URL:
http://eshoponcontainers.westus.cloudapp.azure.com:88/
在基于集群的生产环境中,该URL将映射到集群中使用的负载平衡器,后者反过来将请求分布到各个微服务。在生产环境中,可以在微服务和Internet之间使用类似于Azure应用程序网关的应用程序交付控制器(ADC)。它充当一个透明层,不仅执行负载平衡,而且通过提供SSL终止来保护您的服务。这通过将CPU密集型SSL终止和其他路由职责卸载到Azure应用程序网关来提高主机的负载。在任何情况下,从逻辑应用程序体系结构的角度来看,负载平衡器和ADC都是透明的。
对于一个小型的基于微服务的应用程序来说,直接的客户端到微服务的通信体系结构已经足够好了,特别是如果客户端应用程序是一个服务器端的web应用程序,比如ASP.NET MVC应用程序。然而,当您构建大型复杂的基于微服务的应用程序(例如,当处理数十种微服务类型时),特别是当客户端应用程序是远程移动应用程序或SPA web应用程序时,这种方法面临一些问题。
在基于微服务开发大型应用程序时,请考虑以下问题:
- 客户端应用程序如何将对后端的请求数量减至最小,并减少对多个微服务的聊天通信?
通过与多个微服务交互来构建一个UI屏幕,可以增加互联网上往返的次数。这增加了UI端的延迟和复杂性。理想情况下,应该在服务器端有效地聚合响应。这减少了延迟,因为多个数据块并行返回,一些UI可以在数据准备好后立即显示数据。
- 如何处理交叉关注点,如授权、数据转换和动态请求分派?
在每个微服务上实现安全性和交叉关注点(如安全性和授权)可能需要大量的开发工作。一种可能的方法是将这些服务放在Docker主机或内部集群中,限制从外部直接访问它们,并在一个集中的地方实现这些交叉关注点,比如API网关。
- 客户端应用程序如何与使用非互联网友好协议的服务通信?
服务器端使用的协议(如AMQP或二进制协议)通常在客户端应用程序中不受支持。因此,请求必须通过HTTP/HTTPS等协议执行,然后转换为其他协议。在这种情况下,中间人的方法会有所帮助。
- 你如何塑造一个专为移动应用程序设计的外观?
多个微服务的API可能不能很好地满足不同客户端应用程序的需要。例如,移动应用程序的需求可能不同于web应用程序的需求。对于移动应用程序,您可能需要进一步优化,以便数据响应更加高效。您可以通过聚合来自多个微服务的数据并返回一组数据来完成此操作,有时还会在响应中删除移动应用程序不需要的任何数据。当然,你也可以压缩这些数据。同样,在移动应用程序和微服务之间的facade或API对于这个场景也很方便。
为什么要考虑API网关而不是直接的客户机到微服务通信
在微服务架构中,客户端应用程序通常需要使用多个微服务的功能。如果直接执行该消耗,则客户端需要处理对微服务终结点的多个调用。当应用程序发展并引入新的微服务或更新现有的微服务时会发生什么?如果你的应用程序有很多微服务,那么从客户端应用程序处理这么多端点可能是一场噩梦。由于客户端应用程序将耦合到这些内部终结点,因此在未来改进微服务可能会对客户端应用程序造成很大影响。
因此,对于基于微服务的应用程序,具有中间层或间接层(网关)可能非常方便。如果没有API网关,则
客户端应用程序必须直接向微服务发送请求,这会引发问题,例如以下问题:
- 耦合:没有API网关模式,客户端应用程序耦合到内部微服务。客户端应用程序需要知道应用程序的多个区域是如何在微服务中分解的。在对内部微服务进行演化和重构时,这些操作对维护的影响非常严重,因为它们会导致对客户端应用程序的中断更改,因为客户端应用程序直接引用了内部微服务。客户端应用程序需要频繁更新,使得解决方案更难发展。
- 往返次数太多:客户端应用程序中的单个页面/屏幕可能需要多次调用多个服务。这可能导致客户机和服务器之间的多个网络往返,从而增加显著的延迟。在中间级别处理聚合可以提高客户端应用程序的性能和用户体验。
- 安全问题:如果没有网关,所有微服务都必须暴露在“外部世界”中,使得攻击面比隐藏客户端应用程序未直接使用的内部微服务更大。攻击面越小,应用程序就越安全。
- 横切关注点:每个公开发布的微服务都必须处理诸如授权、SSL等关注点。在许多情况下,这些关注点可以在单层中处理,这样内部微服务就简化了。
什么是API网关模式?
当您使用多个客户端应用程序设计和构建大型或复杂的基于微服务的应用程序时,一个好的考虑方法可以是API网关。这是一个为某些微服务组提供单一入口点的服务。它类似于面向对象设计中的Facade模式,但在本例中,它是分布式系统的一部分。API网关模式有时也被称为“前端后端”(backend for frontend,BFF),因为您在考虑客户端应用程序需求的同时构建它。
因此,API网关位于客户端应用程序和微服务之间。它充当反向代理,将请求从客户端路由到服务。它还可以提供额外的横切特性,如身份验证、SSL终止和缓存。
图13显示了一个定制的API网关是如何与一个简化的基于微服务的体系结构(只需几个微服务)相适应的。
图13 使用作为自定义服务实现的API网关
应用程序连接到一个端点,即API网关,配置为将请求转发到各个微服务。在本例中,API网关将作为一个自定义的ASP.NET核心WebHost服务实现,该服务作为一个容器运行。
在该图中,需要强调的是,您将使用一个面向多个不同客户端应用程序的自定义API网关服务。这一事实可能是一个重要的风险,因为您的API网关服务将根据来自客户端应用程序的许多不同需求不断增长和发展。最终,由于这些不同的需求,它将变得臃肿,而且实际上它可能非常类似于单片应用程序或单片服务。这就是为什么我们非常建议将API网关分成多个服务或多个较小的API网关,例如,每个客户端一个app form factor类型。
在实现API网关模式时需要小心。通常,让一个API网关聚合应用程序的所有内部微服务不是一个好主意。如果它这样做了,它将充当一个整体聚合器或编排器,并通过耦合所有微服务来违反微服务自治。
因此,API网关应该根据业务边界和客户端应用程序进行隔离,而不是作为所有内部微服务的单个聚合器。
当将API网关层拆分为多个API网关时,如果应用程序具有多个客户端应用程序,则在标识多个API网关类型时,这可能是一个主轴心,以便您可以针对每个客户端应用程序的需要具有不同的门面。本例是一个名为“Backend for Frontend”(BFF)的模式,其中每个API网关可以为每个客户端应用程序类型提供不同的API,甚至可以基于客户端的形状因子,通过实现下面调用多个内部微服务的特定适配器代码,如下图所示:
图13.1。使用多个自定义API网关
图13.1显示了按客户机类型隔离的API网关;一个用于移动客户机,一个用于web客户机。传统的web应用程序连接到使用web API网关的MVC微服务。该示例描述了一个具有多个细粒度API网关的简化架构。在本例中,为每个API网关标识的边界纯粹基于“Backend for Frontend”(BFF)模式,因此仅基于每个客户端应用程序所需的API。但在更大的应用程序中,您还应该更进一步,基于业务边界创建额外的API网关,作为第二个设计中心。
关于<<API网关模式与直接客户端到微服务通信>>,下一章继续。
额外资源
- Chris Richardson. Pattern: API Gateway / Backend for Front-End
https://microservices.io/patterns/apigateway.html - API Gateway pattern
https://docs.microsoft.com/azure/architecture/microservices/gateway - Aggregation and composition pattern
https://microservices.io/patterns/data/api-composition.html - Azure API Management
https://azure.microsoft.com/services/api-management/ - Udi Dahan. Service Oriented Composition
http://udidahan.com/2014/07/30/service-oriented-composition-with-video/ - Clemens Vasters. Messaging and Microservices at GOTO 2016 (video)
https://www.youtube.com/watch?v=rXi5CLjIQ9k - API Gateway in a Nutshell (ASP.net Core API Gateway Tutorial Series)
https://www.pogsdotnet.com/2018/08/api-gateway-in-nutshell.html