一、API​​网关

在微服务架构中,客户端应用程序通常需要使用多个微服务中的功能。如果直接执行该消耗,则客户端将需要处理多个微服务端点以进行调用。当应用程序发展,引入新的微服务或更新现有的微服务时,会发生什么?如果您的应用程序具有许多微服务,那么处理来自客户端应用程序的如此多的端点可能是一场噩梦,并且由于客户端应用程序将与这些内部端点耦合,因此将来发展微服务可能会对客户端应用程序产生重大影响。

因此,对于基于微服务的应用程序,具有中间级别的间接层(网关)可能非常方便。如果您没有API网关,则客户端应用程序必须直接将请求发送到微服务,这会引发问题,例如以下问题。

  • 耦合:如果没有API网关模式,则客户端应用程序将耦合到内部微服务。客户端应用程序需要知道如何在微服务中分解应用程序的多个区域。在发展和重构将影响客户端应用程序的内部微服务时,由于客户端应用程序需要跟踪多个微服务端点,因此很难维护。
  • 往返次数过多:客户端应用程序中的单个页面/屏幕可能需要多次调用多个服务。这可能导致客户端和服务器之间进行多次网络往返,从而增加了显着的延迟。在中间级别处理聚合可以改善客户端应用程序的性能和用户体验。
  • 安全问题:如果没有网关,则所有微服务都必须暴露于“外部世界”,与隐藏客户端应用程序未直接使用的内部微服务相比,攻击面更大。攻击面越小,应用程序可以越安全。
  • 跨领域关注点:每个公开发布的微服务都必须处理授权,SSL等关注点。在许多情况下,这些关注点可以在单个层中进行处理,从而简化了内部微服务。

Ocelot:轻量级的开源API网关。非常适合通过.NET Core微服务开始学习这种模式

Ocelot是一个基于开源.NET Core的API网关,专门为需要统一进入系统点的微服务体系结构而设计。它轻巧,快速,可扩展,并在许多其他功能中提供路由和身份验证。

选择在eShopOnContainers参考应用程序中使用Ocelot的主要原因是因为它是一个.NET Core轻量级API网关,您可以将其部署到与部署微服务/容器相同的应用程序部署环境中,例如Docker Host, Kubernetes,Service Fabric等,由于它基于.NET Core,因此它是跨平台的,允许您在Linux或Windows上进行部署。

在最初的架构和模式说明部分之后,接下来的部分将说明如何使用Ocelot实现API网关。

什么是API网关模式

当您设计和构建具有多个客户端应用程序的大型或复杂的基于微服务的应用程序时,可以考虑使用API网关作为一种不错的方法这是为某些微服务组提供单个入口点的服务。它类似于外观模式从对象-导向的设计,但在这种情况下,它是一种分布式系统的一部分。API网关模式的一种变体也称为“前端后端”(BFF),因为您可能会根据每个客户端应用程序的不同需求创建多个API网关。

因此,API网关位于客户端应用程序和微服务之间。它充当反向代理,将请求从客户端路由到服务。它还可以提供其他跨领域功能,例如身份验证,SSL终止和缓存。

下图显示了自定义API网关如何适应基于微服务的体系结构。

使用实现为自定义Web API服务的API网关

在前面的示例中,API网关将实现为作为容器运行的自定义Web API或ASP.NET WebHost服务。

重要的是要强调在该图中,您将使用面向多个不同客户端应用程序的单个自定义API网关服务。由于您的API网关服务将根据客户端应用程序的许多不同要求而增长和发展,因此这一事实可能会构成重大风险。最终,由于这些不同的需求,它将变得ated肿,实际上,它可能与整体应用程序或整体服务非常相似。因此,强烈建议将API网关拆分为多个服务或多个较小的API网关,例如,每种尺寸类型。

您需要注意API网关模式。通常,只有一个API网关来聚合应用程序的所有内部微服务不是一个好主意。如果这样做的话,它将充当整体的聚合器或协调器,并通过耦合所有微服务来违反微服务自治。

因此,应根据业务边界和客户端应用程序对API网关进行隔离,而不应将其用作所有内部微服务的单个聚合器。

将API网关层划分为多个API网关时,如果您的应用程序具有多个客户端应用程序,则在确定多种API网关类型时,这可能是主要的枢纽,因此您可以针对每个客户端应用程序的需求使用不同的外观。这种情况是一种称为“后端的后端”(BFF)的模式,其中每个API网关都可以提供针对每种客户端应用类型量身定制的不同API,甚至可能通过实现特定的适配器代码(基于在调用多个内部微服务的情况下实现)的客户端形式因素,如下图所示。

上图显示了具有多个细粒度API网关的简化架构。在这种情况下,为每个API网关确定的边界完全基于“后端为前端”(BFF)模式,因此仅基于每个客户端应用所需的API。但是,在大型应用程序中,您还应该更进一步,并根据业务边界创建其他API网关,这是第二个设计要点。

API网关模式的主要功能

API网关可以提供多种功能。根据产品的不同,它可能会提供更丰富或更简单的功能,但是,任何API网关最重要的基础功能是以下设计模式。

反向代理或网关路由API网关提供反向代理,以将请求(第7层路由,通常是Http请求)重定向或路由到内部微服务的端点。网关为客户端应用程序提供单个端点或URL,然后在内部将请求映射到一组内部微服务。此路由功能有助于使客户端应用程序与微服务脱钩,但是当通过在整体API和客户端应用之间放置API网关来使整体API现代化时,它也非常方便,然后您可以在仍在使用时将新的API添加为新的微服务直到不再将其拆分为许多微服务,才使用传统的单片API。由于使用API​​网关,因此客户端应用程序不会注意到所使用的API是作为内部微服务还是整体API实现的,更重要的是,

有关更多信息,请检查网关路由模式信息。

请求汇总作为网关模式的一部分,您可以将针对多个内部微服务的多个客户端请求(通常是Http请求)聚合到一个客户端请求中。当客户端页面/屏幕需要来自多个微服务的信息时,此模式特别方便。通过这种方法,客户端应用程序将向API网关发送单个请求,API网关将向内部微服务发送多个请求,然后汇总结果并将所有内容发送回客户端应用程序。此设计模式的主要好处和目标是减少客户端应用程序与后端API之间的聊天,这对于微服务所在的数据中心之外的远程应用程序(例如移动应用程序或来自SPA应用程序的请求)尤其重要客户端远程浏览器中的Javascript。

根据您使用的API网关产品,它可能能够执行此聚合。但是,在许多情况下,在API网关的范围内创建聚合微服务更为灵活,因此您可以使用代码(即C#代码)定义聚合。

有关更多信息,请检查网关聚合模式信息。

跨部门关注点或网关卸载根据每个API网关产品提供的功能,您可以将功能从单个微服务转移到网关,这可以通过将跨领域关注点整合到一个层中来简化每个微服务的实现。这对于在每个内部微服务中可能难以正确实现的特殊功能(例如以下功能)特别方便。

  • 认证与授权
  • 服务发现集成
  • 响应缓存
  • 重试策略,断路器和QoS
  • 限速和节流
  • 负载均衡
  • 记录,跟踪,关联
  • 标头,查询字符串和声明转换
  • IP白名单

根据每个实现,API网关产品可能会提供更多的跨领域问题,但这是最常见的功能。例如,Azure API管理提供了其中的大多数功能,以及许多更高级的功能,这些功能对于商业API很有用。但是,对于更简单的方法,Ocelot之类的轻量级API网关非常灵活,因为您可以将其与微服务一起部署到所选环境(任何协调器)中。

有关更多信息,请检查网关卸载模式信息。

模式参考

API网关

http://microservices.io/patterns/apigateway.html

https://docs.microsoft.com/zh-cn/azure/architecture/microservices/gateway

聚集和组成模式

http://microservices.io/patterns/data/api-composition.html

使用Ocelot实施API网关

在参考微服务应用程序eShopOnContainers中,它使用的是Ocelot,因为它是一个简单轻巧的API网关,您可以将其与微服务/容器一起部署到任何地方,例如在eShopOnContainers使用的以下环境中。

  • 本地主机或本地云中的Docker主机
  • Kubernetes群集,本地或托管云(例如Azure Kubernetes Service(AKS))
  • 本地或云中的Service Fabric群集
  • Service Fabric网格,在Azure中作为PaaS / Serverless

设计和设计您的API网关

以下架构图显示了如何在eShopOnContainers中使用Ocelot实现API网关。

该图显示了如何使用“ Docker for Windows”或“ Docker for Mac”将整个应用程序部署到单个Docker主机或开发PC中。但是,部署到任何业务流程中将非常相似,但是图中的任何容器都可以在业务流程中横向扩展,并且应从业务流程中卸载数据库,缓存和消息代理之类的基础架构资产,并部署到高可用的基础架构系统中,例如Azure SQL数据库,Azure Cosmos DB,Azure Redis,Azure Service Bus或任何本地HA群集解决方案。

您也可以在图中注意到,拥有多个API网关可以使多个开发团队在开发和部署其微服务以及他们自己的相关API网关时保持自治(在本例中为Marketing vs. Shopping)。如果您具有单个整体式API网关,则意味着要由多个开发团队进行更新的单个点可以将所有微服务与应用程序的单个部分耦合在一起。

在设计上走得更远,有时,取决于所选的体系结构,细粒度的API网关也可能仅限于单个业务微服务。由企业或领域决定API网关的界限将帮助您获得更好的设计。例如,API网关层中的细粒度对于基于微服务的更高级的复合UI应用程序尤其有用,因为细粒度的API网关的概念类似于UI组合服务。我们将在基于微服务创建复合UI一节中讨论此主题

因此,对于许多中型和大型应用程序,通常使用自定义API网关产品是一种不错的方法,但不是将其用作单个整体式聚合器或唯一的中央自定义API网关,除非该API网关允许该API的多个独立配置区域具有自主微服务的多个开发团队。

示例微服务/容器,以通过API网关重新路由

例如,eShopOnContainers具有大约6种内部微服务类型,这些类型必须通过API网关发布,如下图所示。

对于身份服务,在我们的设计中,我们将其排除在API网关路由之外,但是使用Ocelot,也可以将其作为重新路由列表的一部分。

正如您所知道的那样,所有这些服务当前都实现为ASP.NET Core Web API服务。让我们关注诸如目录微服务代码之类的微服务之一。

您可以看到这是一个非常典型的ASP.NET Core Web API项目,其中包含几个控制器和方法,如以下代码所示。

Http请求最终将运行访问微服务数据库等的C#代码。

关于微服务URL,当将容器部署在本地开发PC(本地Docker主机)中时,每个微服务的容器始终在其dockerfile中指定一个内部端口(通常为端口80),如以下部分dockerfile所示。

来自microsoft / aspnetcore:2.0.5 AS基础
WORKDIR / app
EXPOSE 80

但是该端口是Docker主机内部的端口,因此客户端应用程序无法访问此端口,只能访问docker-compose部署时提供的外部端口(如果有)。

在部署到生产环境中时,不应发布这些外部端口,因为这就是我们使用API​​网关隐藏与微服务的直接通信的原因。

但是,在开发时,能够直接访问微服务/容器并通过Swagger运行它可能会很有用。这就是为什么即使在API网关或客户端应用程序不使用外部端口的情况下,仍在eShopOnContainers中指定外部端口的原因。

这是Catalog微服务的docker-compose.override.yml文件的部分部分的示例。

  catalog.api
  环境
  - ASPNETCORE_ENVIRONMENT =发展
  - ASPNETCORE_URLS = HTTP://0.0.0.0:80
  - 的ConnectionString = YOUR_VALUE
  - ...其它环境变量
  端口
  - 5101:80 重要:在生产环境中,应删除此处保留的外部端口(5101),以进行微服务调试。
  API网关重定向并通过内部端口(80)的访问。

您可以看到docker-compose.override.yml配置中Catalog容器的内部端口如何为端口80,但用于外部访问的端口为5101。但是,使用API​​网关时,应用程序不应使用此端口,仅调试运行和测试Catalog微服务。

请注意,通常不会将docker-compose部署到生产环境中,因为适用于微服务的正确生产部署环境是像Kubernetes或Service Fabric这样的编排器,并且当部署到这些环境时,您将使用不同的配置文件直接发布微服务的任何外部端口,但是您将始终使用API​​网关中的反向代理。

让我们在本地Docker主机中运行目录微服务,方法是运行Visual Studio的完整eShopOnContainers解决方案(它将运行docker-compose文件中的所有服务),或者仅通过CMD中的以下docker-compose命令启动目录微服务。或PowerShell放置在放置docker-compose.yml和docker-compose.override.yml的文件夹中。

docker-compose run –service-ports catalog.api

此命令将仅运行catalog.api服务容器及其在docker-compose.yml中指定的依赖项(在本例中为SQL Server容器和RabbitMQ容器)。

然后,您可以直接访问Catalog微服务并通过Swagger UI查看其方法,而Swagger UI直接通过该“外部”端口(在本例中为http:// localhost:5101)访问

此时,您可以在VS中的C#代码中设置一个断点,使用Swagger UI中公开的方法测试微服务,等等,最后使用“ docker-compose down”命令清理所有内容。

但是,与微服务的这种直接访问通信(在这种情况下,是通过外部端口5101)恰恰是您在应用程序中要通过设置API网关Ocelot的附加间接级别来避免的。因此,客户端应用程序将不会直接访问微服务。

使用Ocelot实施API网关

Ocelot基本上是一组可以按特定顺序应用的中间件。

Ocelot设计为仅与ASP.NET Core一起使用,并且目标是netstandard2.0。这意味着它可以在支持.NET Standard 2.0的任何地方使用,包括.NET Core 2.0运行时和.NET Framework 4.6.1运行时及更高版本。

您可以使用Ocelot NuGet软件包在您的ASP.NET Core项目中安装Ocelot及其依赖项。

安装包豹猫

对于eShopOnContainers,其API网关实现是一个非常简单的ASP.NET Core WebHost项目,并且Ocelot的中间件处理了所有API网关功能,如下图所示。

这个ASP.NET Core WebHost项目基本上由两个简单的文件Program.cs和Startup.cs组成。

Program.cs只需要创建和配置典型的ASP.NET Core BuildWebHost。

对于Ocelot而言,重要的一点是必须通过AddJsonFile()方法提供给构建器configuration.json文件。在configuration.json文件中,您可以指定所有API网关重新路由,这意味着外部端点和端口以及相关的内部端点和内部端口。

{
“重新路由”:[],

“全局配置”:{}
}

该配置分为两部分。ReRoutes和GlobalConfiguration的数组。ReRoutes是告诉Ocelot如何处理上游请求的对象。全局配置允许覆盖“重新路由”特定设置。如果您不想管理很多ReRoute特定设置,这将很有用。

这是来自eShopOnContainers(web-bff-shopping)的API网关之一的ReRoute 配置文件的简化示例

  {
  重新路由:[
  {
  DownstreamPathTemplate / api / {version} / {everything}
  DownstreamScheme http
  DownstreamHostAndPorts :[
  {
  主机 catalog.api
  端口 80
  }
  ],
  UpstreamPathTemplate / api / {version} / c / {everything}
  UpstreamHttpMethod :[ POST PUT GET ]
  },
  {
  DownstreamPathTemplate / api / {version} / {everything}
  DownstreamScheme http
  DownstreamHostAndPorts :[
  {
  主机 basket.api
  端口 80
  }
  ],
  UpstreamPathTemplate / api / {version} / b / {everything}
  UpstreamHttpMethod :[ POST PUT GET ],
  AuthenticationOptions :{
  AuthenticationProviderKey IdentityApiKey
  AllowedScopes :[]
  }
  }
   
  ],
  GlobalConfiguration :{
  RequestIdKey OcRequestId
  AdministrationPath / administration
  }
  }

Ocelot API网关的主要功能是接收传入的HTTP请求并将它们转发到下游服务(当前作为另一个HTTP请求)。Ocelot描述了将一个请求路由到另一个请求作为ReRoute。

例如,让我们从上方着眼于configuration.json中的ReRoutes之一,即Basket微服务的配置。

  {
  DownstreamPathTemplate / api / {version} / {everything}
  DownstreamScheme http
  DownstreamHostAndPorts :[
  {
  主机 basket.api
  端口 80
  }
  ],
  UpstreamPathTemplate / api / {version} / b / {everything}
  UpstreamHttpMethod :[ POST PUT GET ],
  AuthenticationOptions :{
  AuthenticationProviderKey IdentityApiKey
  AllowedScopes :[]
  }
  }

DownstreamPathTemplate,Scheme和DownstreamHostAndPorts构成了将请求转发到的内部微服务URL。

该端口是服务使用的内部端口。使用容器时,在其dockerfile中指定的端口。

主机将是一个服务名称,将取决于您使用的服务名称解析。使用docker-compose时,服务名称由Docker主机提供,而Docker主机使用docker-compose文件中提供的服务名称。如果使用像Kubernetes或Service Fabric这样的协调器,则该名称应由每个协调器提供的DNS或名称解析来解析。

DownstreamHostAndPorts是一个数组,其中包含您希望将请求转发到的任何下游服务的主机和端口。通常,它仅包含一个条目,但是有时您可能希望对下游服务进行负载均衡请求,Ocelot允许您添加多个条目,然后选择一个负载均衡器。但是,如果使用Azure和任何协调器,则最好是通过云和协调器基础结构实现负载平衡。

UpstreamPathTemplate是Ocelot将用于标识对客户端的给定请求使用哪个DownstreamPathTemplate的URL。最后,使用UpstreamHttpMethod,以便Ocelot可以区分对同一URL的不同请求(GET,POST,PUT),并且显然需要工作。

此时,您可以使用一个或多个合并的configuration.json文件使用单个Ocelot API网关(ASP.NET Core WebHost),也可以将配置存储在Consul KV存储中

但是,正如在架构和设计部分中介绍的那样,如果您真的想拥有自治的微服务,可能最好将单个整体式API网关拆分为多个API网关和/或BFF(前端后端)。为此,让我们看看如何使用Docker容器实现该方法。

使用单个Docker容器映像来运行多个不同的API Gateway / BFF容器

在eShopOnContainers中,我们利用Ocelot API网关利用单个Docker容器映像,但是在运行时,我们通过为每种API-Gateway / BFF类型提供不同的configuration.json文件,为每种类型的API创建不同的服务/容器。容器,在运行时。

因此,在eShopOnContainers中,将使用名为“ OcelotApiGw”的项目以及在docker-compose.yml文件中指定的图像名称“ eshop / ocelotapigw”创建“通用Ocelot API网关Docker映像”。然后,当部署到Docker时,将从同一Docker映像创建四个API-Gateway容器,如以下docker-compose.yml文件的摘录所示。

https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/docker-compose.yml

此外,正如您在docker-compose.override.yml文件中所看到的那样,这些API Gateway容器之间的唯一区别是Ocelot配置文件,该文件对于每个服务容器都是不同的,并且是在运行时通过Docker卷指定的,如以下docker-compose.override.yml文件。

通过将API网关拆分为多个API网关,专注于微服务的不同子集的不同开发团队可以通过使用独立的Ocelot配置文件来管理自己的API网关,同时重新使用同一Ocelot Docker映像。

现在,如果您运行带有API网关的eShopOnContainers(打开eShopOnContainers-ServicesAndWebApps.sln解决方案时默认包含在VS中,或者运行“ docker-compose up”),则将执行以下示例路由。

例如,当访问由webshoppingapigw API网关提供的上游URL http:// localhost:5202/api/v1/c/catalog/items/2/时,您将从内部下游URL http:// catalog获得结果 Docker主机中的api / api / v1 / 2,如以下浏览器中所示。

由于测试或调试的原因,如果您想直接访问目录Docker容器(仅在开发环境中)而不通过API网关,因为“ catalog.api”是Docker主机内部的DNS解析(服务发现)由docker-compose服务名称处理),直接访问容器的唯一方法是通过docker-compose.override.yml中发布的外部端口,该端口仅用于开发测试,例如http://localhost:5101/api/v1/Catalog/items/1在以下浏览器中。

但是应用程序已配置为可以通过API网关访问所有微服务,而不是通过直接端口“快捷方式”访问。

eShopOnContainers中的网关聚合模式

如前所述,实现请求聚合的一种非常灵活的方法是通过代码使用自定义服务。您还可以使用Ocelot中的“ 请求聚合”功能来实现请求聚合,但是它可能没有您所需要的灵活。因此,在eShopOnContainers中实现聚合的所选方法是为每个聚合器使用显式的ASP.NET Core Web API服务。因此,考虑到聚合器服务(实际上未在先前显示的简化全局体系结构图中显示)时,API网关组成图实际上在某种程度上得到了扩展。在下图中,您还可以看到聚合器服务如何与其相关的API网关一起使用。

放大下图所示的图表,您会注意到在“购物”业务领域中,如何通过在API网关领域实现那些聚合器服务来减少与微服务的闲聊,从而改善客户端应用程序。

您会注意到,当该图显示来自API网关的可能请求时,它会变得非常复杂。尽管您可以从客户端应用程序的角度看到如何简化蓝色箭头,但是在使用聚合器模式时,通过减少通信中的聊天和延迟,最终可以显着改善远程应用程序(移动和SPA应用程序)的用户体验,特别。

对于“市场营销”业务领域和微服务而言,这是一个非常简单的用例,因此不需要使用聚合器,但是如果需要,也可以使用聚合器。

Ocelot API网关中的身份验证和授权

在Ocelot API网关中,您可以在API网关外部或内部放置身份验证服务,例如使用IdentityServer提供身份验证令牌的ASP.NET Core Web API服务

由于在eShopOnContainers中使用基于BFF和业务区域划分边界的多个API网关,因此身份/身份验证服务被排除在API网关之外,如下图以黄色突出显示。

但是,Ocelot还支持将Identity / Auth微服务放置在API网关边界内,如该其他图中所示。

由于在eShopOnContainers中,我们已将API网关划分为多个BFF(前端后端)和业务区域API网关,因此另一种选择是创建一个额外的API网关来解决跨领域问题。在具有多个跨领域关注的微服务的更复杂的基于微服务的体系结构中,这种选择将是公平的。但是,我们在eShopOnContainers中只有一个跨领域的问题,为简单起见,决定只在API网关领域处理安全服务。

无论如何,当尝试使用任何安全的微服务(如果在API网关级别受保护)时,将首先访问Ocelot API网关的身份验证模块。这将重定向到访问Identity或auth微服务以获取访问令牌,因此您可以使用access_token访问受保护的服务。

在API网关级别对任何服务进行身份验证的方式是通过在configuration.json的相关设置中设置AuthenticationProviderKey

Ocelot运行时,它将查看ReRoutes AuthenticationOptions.AuthenticationProviderKey并检查是否存在使用给定密钥注册的身份验证提供程序。如果没有,Ocelot将不会启动。如果存在,则ReRoute在执行时将使用该提供程序。

因为Ocelot WebHost配置有authenticationProviderKey = “ IdentityApiKey”,所以只要该服务有任何请求而没有任何身份验证令牌就将需要身份验证。

然后,还需要在要访问的任何资源(例如微服务)上,使用[Authorize]属性设置授权,例如在下面的Basket微服务控制器中。

诸如“购物篮”之类的ValidAudiences与每个微服务中通过Startup类ConfigureServices()处的AddJwtBearer()定义的受众相关联,例如以下代码。

现在,如果您尝试使用基于API网关(例如http:// localhost:5202 / api / v1 / b / basket / 1)的ReRoute URL访问任何安全的微服务(如购物篮微服务),
那么除非得到401未经授权,否则您提供有效的令牌。另一方面,如果对ReRoute URL进行了身份验证,Ocelot将调用与其关联的任何下游方案(内部微服务URL)。

在Ocelot的ReRoutes层进行授权Ocelot支持在身份验证后评估的基于声明的授权。通过在ReRoute配置中添加以下内容,可以在路由级别上设置授权。

“ RouteClaimsRequirement”:{
“ UserType”:“员工”
}

在该示例中,当调用授权中间件时,Ocelot将查找用户在令牌中是否具有声明类型“ UserType”,以及该声明的值为“ employee”。如果不是,则将不授权用户,并且响应将被禁止403。

点击查看原文

原文地址:https://www.cnblogs.com/xuxml/p/12017518.html