Freigeben über


Implementieren von API-Gateways mit Ocelot

Tipp

Dieser Inhalt ist ein Auszug aus dem eBook .NET Microservices Architecture for Containerized .NET Applications, verfügbar auf .NET Docs oder als kostenlose herunterladbare PDF, die offline gelesen werden kann.

.NET-Mikroservices-Architektur für containerisierte .NET-Anwendungen eBook-Cover-Miniaturansicht.

Von Bedeutung

Die Referenz microservice-Anwendung eShopOnContainers verwendet derzeit Features, die von Envoy bereitgestellt werden, um das API-Gateway anstelle des früher referenzierten Ocelot zu implementieren. Wir haben diese Designauswahl aufgrund der integrierten Unterstützung von Envoy für das WebSocket-Protokoll getroffen, die von der neuen gRPC-Inter-Service-Kommunikation in eShopOnContainers implementiert wird. Wir haben diesen Abschnitt jedoch in der Anleitung beibehalten, damit Sie Ocelot als einfaches, fähiges und einfaches API-Gateway betrachten können, das für Szenarien auf Produktionsniveau geeignet ist. Außerdem enthält die neueste Ocelot-Version eine grundlegende Änderung des JSON-Schemas. Erwägen Sie die Verwendung von Ocelot < v16.0.0 oder verwenden Sie die Schlüsselrouten anstelle von "ReRoutes".

Entwerfen und Entwerfen Ihrer API-Gateways

Das folgende Architekturdiagramm zeigt, wie API-Gateways mit Ocelot in eShopOnContainers implementiert wurden.

Diagramm mit der eShopOnContainers-Architektur.

Abbildung 6-28. eShopOnContainers-Architektur mit API-Gateways

Dieses Diagramm zeigt, wie die gesamte Anwendung in einem einzelnen Docker-Host oder Entwicklungs-PC mit "Docker für Windows" oder "Docker für Mac" bereitgestellt wird. Die Bereitstellung in jedem Orchestrator wäre jedoch ähnlich, aber jeder Container im Diagramm könnte im Orchestrator skaliert werden.

Darüber hinaus sollten die Infrastrukturressourcen wie Datenbanken, Cache und Nachrichtenbroker vom Orchestrator ausgelagert und in hoch verfügbaren Systemen für die Infrastruktur bereitgestellt werden, z. B. Azure SQL-Datenbank, Azure Cosmos DB, Azure Redis, Azure Service Bus oder lokale HA-Clustering-Lösung.

Wie Sie auch im Diagramm feststellen können, können mehrere API-Gateways mehrere Entwicklungsteams autonom sein (in diesem Fall Marketingfeatures vs. Shopping-Features), wenn sie ihre Microservices entwickeln und bereitstellen, sowie ihre eigenen zugehörigen API-Gateways.

Wenn Sie ein einzelnes monolithisches API-Gateway hatten, das einen einzelnen Punkt bedeuten würde, der von mehreren Entwicklungsteams aktualisiert werden kann, was alle Microservices mit einem einzigen Teil der Anwendung zusammenhlechten könnte.

Je nach ausgewählter Architektur kann auch ein feinkörniges API-Gateway wesentlich weiter auf einen einzelnen Business-Microservice beschränkt werden. Wenn Sie die Grenzen des API-Gateways durch das Unternehmen oder die Domäne diktieren, können Sie ein besseres Design erzielen.

Beispielsweise kann eine feine Granularität auf der API-Gatewayebene besonders nützlich für komplexere zusammengesetzte UI-Anwendungen sein, die auf Microservices basieren, da das Konzept eines feinkörnigen API-Gateways mit einem Benutzeroberflächenkompositionsdienst vergleichbar ist.

Im vorherigen Abschnitt erstellen wir zusammengesetzte UI basierend auf Microservices.

Bei vielen anwendungen mittlerer und großer Größe ist die Verwendung eines benutzerdefinierten API-Gateway-Produkts in der Regel ein guter Ansatz, aber nicht als einzelner monolithischer Aggregator oder ein eindeutiges zentrales benutzerdefiniertes API-Gateway, es sei denn, dass das API-Gateway mehrere unabhängige Konfigurationsbereiche für die verschiedenen Entwicklungsteams ermöglicht, die autonome Microservices erstellen.

Beispiel für Microservices/Container zum Umleiten über die API-Gateways

Beispielsweise hat eShopOnContainers etwa sechs interne Microservice-Typen, die über die API-Gateways veröffentlicht werden müssen, wie in der folgenden Abbildung dargestellt.

Screenshot des Ordners

Abbildung 6-29. Microservice-Ordner in eShopOnContainers-Projektmappe in Visual Studio

Über den Identitätsdienst wird im Entwurf das API-Gatewayrouting verlassen, da es sich um das einzige grenzüberschreitende Problem im System handelt, obwohl bei Ocelot es auch möglich ist, ihn als Teil der Umleitungslisten einzuschließen.

Alle diese Dienste werden derzeit als ASP.NET Core Web API-Dienste implementiert, wie Sie aus dem Code erkennen können. Konzentrieren wir uns auf einen der Microservices wie den Katalog-Microservice-Code.

Screenshot des Projektmappen-Explorers mit dem Projektinhalt

Abbildung 6-30. Beispielweb-API-Microservice (Katalog-Microservice)

Sie können sehen, dass der Catalog microservice ein typisches ASP.NET Core Web API-Projekt mit mehreren Controllern und Methoden wie im folgenden Code ist.

[HttpGet]
[Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType(typeof(CatalogItem),(int)HttpStatusCode.OK)]
public async Task<IActionResult> GetItemById(int id)
{
    if (id <= 0)
    {
        return BadRequest();
    }
    var item = await _catalogContext.CatalogItems.
                                          SingleOrDefaultAsync(ci => ci.Id == id);
    //…

    if (item != null)
    {
        return Ok(item);
    }
    return NotFound();
}

Die HTTP-Anforderung führt diese Art von C#-Code aus, der auf die Microservice-Datenbank und alle zusätzlichen erforderlichen Aktionen zugreift.

In Bezug auf die Microservice-URL, wenn die Container auf Ihrem lokalen Entwicklungs-PC (lokaler Docker-Host) bereitgestellt werden, verfügt jeder Microservice-Container immer über einen internen Port (normalerweise Port 80), der in seiner Dockerfile angegeben ist, wie in der folgenden Dockerfile-Datei:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

Der im Code angezeigte Port 80 ist innerhalb des Docker-Hosts intern, sodass er von Client-Apps nicht erreicht werden kann.

Client-Apps können nur auf die externen Ports (sofern vorhanden) zugreifen, die bei der Bereitstellung mit docker-composeveröffentlicht wurden.

Diese externen Ports sollten beim Bereitstellen in einer Produktionsumgebung nicht veröffentlicht werden. Aus diesem speziellen Grund sollten Sie das API-Gateway verwenden, um die direkte Kommunikation zwischen den Client-Apps und den Microservices zu vermeiden.

Bei der Entwicklung möchten Sie jedoch direkt auf den Microservice/Container zugreifen und ihn über Swagger ausführen. Aus diesem Grund werden in eShopOnContainers die externen Ports weiterhin angegeben, auch wenn sie nicht vom API-Gateway oder den Client-Apps verwendet werden.

Hier ist ein Beispiel für die docker-compose.override.yml Datei für den Katalog-Microservice:

catalog-api:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - ASPNETCORE_URLS=http://0.0.0.0:80
    - ConnectionString=YOUR_VALUE
    - ... Other Environment Variables
  ports:
    - "5101:80"   # Important: In a production environment you should remove the external port (5101) kept here for microservice debugging purposes.
                  # The API Gateway redirects and access through the internal port (80).

Sie können sehen, wie in der docker-compose.override.yml Konfiguration der interne Port für den Katalogcontainer Port 80 ist, aber der Port für den externen Zugriff ist 5101. Dieser Port sollte jedoch nicht von der Anwendung verwendet werden, wenn ein API-Gateway verwendet wird, nur zum Debuggen, Ausführen und Testen nur des Katalog-Microservice.

Normalerweise stellen Sie docker-compose nicht in einer Produktionsumgebung bereit, da die richtige Produktionsbereitstellungsumgebung für Microservices ein Orchestrator wie Kubernetes oder Service Fabric ist. Bei der Bereitstellung in diesen Umgebungen verwenden Sie unterschiedliche Konfigurationsdateien, bei denen Sie keinen direkten externen Port für die Microservices veröffentlichen, sondern immer den Reverseproxy vom API-Gateway verwenden.

Führen Sie den Katalog-Microservice auf Ihrem lokalen Docker-Host aus. Führen Sie entweder die vollständige eShopOnContainers-Lösung aus Visual Studio aus (sie führt alle Dienste in den Docker-Compose-Dateien aus), oder starten Sie den Katalog-Microservice mit dem folgenden Docker-Compose-Befehl in CMD oder PowerShell, der sich an dem Ordner befindet, in dem die docker-compose.yml Datei platziert ist.docker-compose.override.yml

docker-compose run --service-ports catalog-api

Dieser Befehl führt nur den Katalog-API-Dienstcontainer sowie Abhängigkeiten aus, die in der docker-compose.yml angegeben sind. In diesem Fall der SQL Server-Container und der RabbitMQ-Container.

Anschließend können Sie direkt auf den Catalog microservice zugreifen und seine Methoden über die Swagger UI direkt über diesen "externen" Port aufrufen, in diesem Fall http://host.docker.internal:5101/swagger:

Screenshot der Swagger-BEnutzeroberfläche mit der Rest-API

Abbildung 6-31. Testen des Catalog microservice mit seiner Swagger UI

An diesem Punkt könnten Sie einen Haltepunkt in C#-Code in Visual Studio festlegen, den Microservice mit den Methoden testen, die in der Swagger-Benutzeroberfläche verfügbar gemacht werden, und schließlich alles mit dem docker-compose down Befehl bereinigen.

In diesem Fall über den externen Port 5101 ist jedoch die direkte Kommunikation mit dem Microservice genau das, was Sie in Ihrer Anwendung vermeiden möchten. Und Sie können dies vermeiden, indem Sie die zusätzliche Dereferenzierungsebene des API-Gateways (Ocelot, in diesem Fall) festlegen. Auf diese Weise greift die Client-App nicht direkt auf den Microservice zu.

Implementieren Von API-Gateways mit Ocelot

Ocelot ist im Grunde eine Reihe von Middleware, die Sie in einer bestimmten Reihenfolge anwenden können.

Ocelot ist so konzipiert, dass es nur mit ASP.NET Core funktioniert. Die neueste Version des Pakets ist 18.0, die auf .NET 6 ausgerichtet ist und daher nicht für .NET Framework-Anwendungen geeignet ist.

Sie installieren Ocelot und dessen Abhängigkeiten in Ihrem ASP.NET Core-Projekt mit dem NuGet-Paket von Ocelot aus Visual Studio.

Install-Package Ocelot

In eShopOnContainers ist die API-Gatewayimplementierung ein einfaches ASP.NET Core WebHost-Projekt, und Die Middleware von Ocelot verarbeitet alle API-Gateway-Features, wie in der folgenden Abbildung dargestellt:

Screenshot des Projektmappen-Explorers mit dem Ocelot-API-Gatewayprojekt.

Abbildung 6-32. Das Basisprojekt OcelotApiGw in eShopOnContainers

Dieses ASP.NET Core WebHost-Projekt wird mit zwei einfachen Dateien erstellt: Program.cs und Startup.cs.

Die Program.cs muss lediglich die typische ASP.NET Core BuildWebHost erstellen und konfigurieren.

namespace OcelotApiGw
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            var builder = WebHost.CreateDefaultBuilder(args);

            builder.ConfigureServices(s => s.AddSingleton(builder))
                    .ConfigureAppConfiguration(
                          ic => ic.AddJsonFile(Path.Combine("configuration",
                                                            "configuration.json")))
                    .UseStartup<Startup>();
            var host = builder.Build();
            return host;
        }
    }
}

Der wichtige Punkt hier für Ocelot ist die configuration.json Datei, die Sie dem Generator über die AddJsonFile() Methode bereitstellen müssen. Hier configuration.json geben Sie alle API-Gateway-ReRoutes an, was bedeutet, dass die externen Endpunkte mit bestimmten Ports und den korrelierten internen Endpunkten in der Regel unterschiedliche Ports verwenden.

{
    "ReRoutes": [],
    "GlobalConfiguration": {}
}

Es gibt zwei Abschnitte für die Konfiguration. Ein Array von ReRoutes und einer GlobalConfiguration. Bei den ReRoutes handelt es sich um die Objekte, die Ocelot mitteilen, wie eine upstream-Anforderung behandelt wird. Die globale Konfiguration ermöglicht Außerkraftsetzungen von ReRoute-spezifischen Einstellungen. Es ist hilfreich, wenn Sie nicht viele reRoute-spezifische Einstellungen verwalten möchten.

Hier ist ein vereinfachtes Beispiel für die ReRoute-Konfigurationsdatei aus einem der API-Gateways von eShopOnContainers.

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "catalog-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/c/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

  ],
    "GlobalConfiguration": {
      "RequestIdKey": "OcRequestId",
      "AdministrationPath": "/administration"
    }
  }

Die Hauptfunktionalität eines Ocelot-API-Gateways besteht darin, eingehende HTTP-Anforderungen zu übernehmen und an einen nachgeschalteten Dienst weiterzuleiten, derzeit als eine andere HTTP-Anforderung. Ocelot beschreibt das Routing einer Anforderung an eine andere als ReRoute.

Konzentrieren wir uns beispielsweise auf eine der ReRoutes im configuration.json von oben, die Konfiguration für den Basket microservice.

{
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
}

Die DownstreamPathTemplate-, Schema- und DownstreamHostAndPorts stellen die interne Microservice-URL, an die diese Anforderung weitergeleitet wird.

Der Port ist der interne Port, der vom Dienst verwendet wird. Bei der Verwendung von Containern, der in der Dockerfile-Datei angegebene Port.

Dies Host ist ein Dienstname, der von der verwendeten Dienstnamenauflösung abhängt. Bei Verwendung von Docker-Compose werden die Dienstnamen vom Docker-Host bereitgestellt, der die in den Docker-Compose-Dateien bereitgestellten Dienstnamen verwendet. Wenn Sie einen Orchestrator wie Kubernetes oder Service Fabric verwenden, sollte dieser Name durch die DNS- oder Namensauflösung aufgelöst werden, die von jedem Orchestrator bereitgestellt wird.

DownstreamHostAndPorts ist ein Array, das den Host und Port aller nachgeschalteten Dienste enthält, an die Sie Anforderungen weiterleiten möchten. In der Regel enthält diese Konfiguration nur einen Eintrag, aber manchmal möchten Sie möglicherweise Lastenausgleichsanforderungen an Ihre downstream-Dienste laden, und Ocelot ermöglicht es Ihnen, mehrere Einträge hinzuzufügen und dann ein Lastenausgleichsmodul auszuwählen. Wenn Sie Jedoch Azure und einen Orchestrator verwenden, empfiehlt es sich wahrscheinlich, den Lastenausgleich mit der Cloud- und Orchestratorinfrastruktur zu laden.

Die UpstreamPathTemplate ist die URL, die Ocelot verwendet, um zu identifizieren, welche DownstreamPathTemplate für eine bestimmte Anforderung vom Client verwendet werden soll. Schließlich wird die UpstreamHttpMethod verwendet, damit Ocelot zwischen verschiedenen Anforderungen (GET, POST, PUT) mit derselben URL unterscheiden kann.

An diesem Punkt könnten Sie über ein einzelnes Ocelot-API-Gateway (ASP.NET Core WebHost) mit einer oder mehreren zusammengeführten configuration.json Dateien verfügen, oder Sie können die Konfiguration auch in einem Konsul-KV-Speicher speichern.

Aber wie in den Bereichen Architektur und Design eingeführt, wenn Sie wirklich autonome Microservices haben möchten, ist es möglicherweise besser, dieses einzelne monolithische API-Gateway in mehrere API-Gateways und/oder BFF (Back-End für Frontend) aufzuteilen. Sehen wir uns zu diesem Zweck an, wie dieser Ansatz mit Docker-Containern implementiert wird.

Verwenden eines einzelnen Docker-Containerimages zum Ausführen mehrerer verschiedener API-Gateway-/BFF-Containertypen

In eShopOnContainers verwenden wir ein einzelnes Docker-Containerimage mit dem Ocelot-API-Gateway, erstellen jedoch zur Laufzeit unterschiedliche Dienste/Container für jeden API-Gateway/BFF-Typ, indem wir eine andere configuration.json Datei bereitstellen, indem ein Docker-Volume verwendet wird, um auf einen anderen PC-Ordner für jeden Dienst zuzugreifen.

Diagramm eines einzelnen Ocelot-Gateway-Docker-Images für alle API-Gateways.

Abbildung 6-33. Erneutes Verwenden eines einzelnen Ocelot Docker-Images für mehrere API-Gatewaytypen

In eShopOnContainers wird das "Generic Ocelot API Gateway Docker Image" mit dem Projekt "OcelotApiGw" und dem Imagenamen "eshop/ocelotapigw" erstellt, der in der docker-compose.yml-Datei angegeben ist. Wenn Sie dann in Docker bereitstellen, werden vier API-Gateway Container aus demselben Docker-Image erstellt, wie im folgenden Extrakt aus der docker-compose.yml-Datei gezeigt.

  mobileshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  mobilemarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webmarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

Wie Sie in der folgenden docker-compose.override.yml-Datei sehen können, ist der einzige Unterschied zwischen diesen API-Gatewaycontainern die Ocelot-Konfigurationsdatei, die für jeden Dienstcontainer unterschiedlich ist und zur Laufzeit über ein Docker-Volume angegeben wird.

mobileshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5200:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Shopping/apigw:/app/configuration

mobilemarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5201:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Marketing/apigw:/app/configuration

webshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5202:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Shopping/apigw:/app/configuration

webmarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5203:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Marketing/apigw:/app/configuration

Aufgrund dieses vorherigen Codes und wie im folgenden Visual Studio-Explorer gezeigt, ist die einzige Datei, die zum Definieren der einzelnen Geschäfts-/BFF-API-Gateways erforderlich ist, nur eine configuration.json Datei, da die vier API-Gateways auf demselben Docker-Image basieren.

Screenshot aller API-Gateways mit configuration.json Dateien.

Abbildung 6-34. Die einzige Datei, die zum Definieren jedes API-Gateways/BFF mit Ocelot erforderlich ist, ist eine Konfigurationsdatei.

Durch das Aufteilen des API-Gateways in mehrere API-Gateways können verschiedene Entwicklungsteams, die sich auf verschiedene Teilmengen von Microservices konzentrieren, ihre eigenen API-Gateways mithilfe unabhängiger Ocelot-Konfigurationsdateien verwalten. Außerdem können sie gleichzeitig dasselbe Ocelot Docker-Image wiederverwenden.

Wenn Sie nun eShopOnContainers mit den API-Gateways ausführen (standardmäßig in VS enthalten, wenn sie eShopOnContainers-ServicesAndWebApps.sln Lösung öffnen oder "docker-compose up" ausführen), werden die folgenden Beispielrouten ausgeführt.

Wenn Sie beispielsweise die upstream-URL http://host.docker.internal:5202/api/v1/c/catalog/items/2/ besuchen, die vom Webshoppingapigw-API-Gateway bereitgestellt wird, erhalten Sie dasselbe Ergebnis von der internen Downstream-URL http://catalog-api/api/v1/2 innerhalb des Docker-Hosts, wie im folgenden Browser.

Screenshot eines Browsers mit einer Antwort, die über das API-Gateway geht.

Abbildung 6-35. Zugreifen auf einen Microservice über eine VOM API-Gateway bereitgestellte URL

Aus Test- oder Debugginggründen, wenn Sie direkt auf den Katalog-Docker-Container (nur in der Entwicklungsumgebung) zugreifen möchten, ohne das API-Gateway zu übergeben, da es sich bei der "Katalog-API" um eine interne DNS-Auflösung für den Docker-Host handelt (Dienstermittlung, die von Docker-Compose-Dienstnamen behandelt wird), besteht die einzige Möglichkeit, direkt auf den Container zuzugreifen, über den externen Port, der im docker-compose.override.yml veröffentlicht wurde, die nur für Entwicklungstests bereitgestellt wird, z http://host.docker.internal:5101/api/v1/Catalog/items/1 . B. im folgenden Browser.

Screenshot eines Browsers mit einer direkten Antwort auf die Catalog.api.

Abbildung 6-36. Direkter Zugriff auf einen Microservice zu Testzwecken

Die Anwendung ist jedoch so konfiguriert, dass sie über die API-Gateways auf alle Microservices zugreift, nicht über die direkten Portverknüpfungen.

Das Gatewayaggregationsmuster in eShopOnContainers

Wie bereits zuvor eingeführt, ist eine flexible Möglichkeit zum Implementieren der Anforderungsaggregation mit benutzerdefinierten Diensten nach Code. Die ausgewählte Methode zum Implementieren der Aggregation in eShopOnContainers besteht aus einem expliziten ASP.NET Core Web API-Dienst für jeden Aggregator.

Gemäß diesem Ansatz ist das API-Gateway-Kompositionsdiagramm in Wirklichkeit etwas erweitert, wenn die Aggregatordienste berücksichtigt werden, die nicht im vereinfachten globalen Architekturdiagramm dargestellt werden.

Im folgenden Diagramm können Sie auch sehen, wie die Aggregatordienste mit ihren zugehörigen API-Gateways funktionieren.

Diagramm der eShopOnContainers-Architektur mit Aggregatordiensten.

Abbildung 6-37. eShopOnContainers-Architektur mit Aggregatordiensten

Wenn Sie den Geschäftsbereich "Shopping" weiter vergrößern, können Sie in der folgenden Abbildung sehen, dass die Chattigkeit zwischen den Client-Apps und den Microservices reduziert wird, wenn sie die Aggregatordienste in den API-Gateways verwenden.

Diagramm, in dem die Architektur von eShopOnContainers vergrößert wird.

Abbildung 6-38. Zoomen der Aggregatordienste

Sie können feststellen, wie das Diagramm zeigt, wie die möglichen Anforderungen, die von den API-Gateways stammen, komplex werden können. Wenn Sie jedoch das Aggregatormuster verwenden, können Sie sehen, wie die Pfeile in Blau die Kommunikation aus einer Client-App-Perspektive vereinfachen würden. Dieses Muster hilft nicht nur, die Chattigkeit und Latenz in der Kommunikation zu reduzieren, es verbessert auch die Benutzerfreundlichkeit für die Remote-Apps (mobile und SPA-Apps).

Im Falle des "Marketing"-Geschäftsbereichs und microservices ist es ein einfacher Anwendungsfall, so dass es nicht erforderlich war, Aggregatoren zu verwenden, aber es könnte bei Bedarf auch möglich sein.

Authentifizierung und Autorisierung in Ocelot-API-Gateways

In einem Ocelot-API-Gateway können Sie den Authentifizierungsdienst, z. B. einen ASP.NET Core Web API-Dienst, verwenden , der das Authentifizierungstoken bereitstellt, entweder außerhalb oder innerhalb des API-Gateways.

Da eShopOnContainers mehrere API-Gateways mit Grenzen basierend auf BFF und Geschäftsbereichen verwendet, wird der Identitäts-/Authentifizierungsdienst aus den API-Gateways weggelassen, wie im folgenden Diagramm gelb hervorgehoben.

Diagramm, das identitäts microservice unter dem API-Gateway zeigt.

Abbildung 6-39. Position des Identitätsdiensts in eShopOnContainers

Ocelot unterstützt jedoch auch das Sitzen des Identitäts-/Auth-Mikroservice innerhalb der API-Gatewaygrenze, wie in diesem anderen Diagramm.

Diagramm, das die Authentifizierung in einem Ocelot-API-Gateway zeigt.

Abbildung 6-40. Authentifizierung in Ocelot

Wie das vorherige Diagramm zeigt, fordert die Identity microservice unter dem API-Gateway (AG): 1) AG ein Authentifizierungstoken von Identity Microservice an, 2) Der Identitäts-Microservice gibt token an AG, 3-4) AG-Anforderungen von Microservices mithilfe des Authentifizierungstokens zurück. Da die eShopOnContainers-Anwendung das API-Gateway in mehrere BFF -Gateways (Back-End für Frontend) und API-Gateways für Geschäftsbereiche aufgeteilt hat, wäre eine weitere Option darin gewesen, ein zusätzliches API-Gateway für grenzüberschreitende Bedenken zu erstellen. Diese Wahl wäre in einer komplexeren Microservice-basierten Architektur mit mehreren querschnittsübergreifenden Anliegen Microservices fair. Da es in eShopOnContainers nur ein querschnittsübergreifendes Problem gibt, wurde beschlossen, den Sicherheitsdienst einfach aus dem API-Gatewaybereich zu behandeln, um die Einfachheit halber zu gewährleisten.

Wenn die App auf API-Gateway-Ebene gesichert ist, wird das Authentifizierungsmodul des Ocelot-API-Gateways zuerst besucht, wenn Sie versuchen, einen gesicherten Microservice zu verwenden. Dadurch wird die HTTP-Anforderung umgeleitet, um den Identitäts- oder Authentifizierungs-Microservice zu besuchen, um das Zugriffstoken abzurufen, damit Sie die geschützten Dienste mit dem access_token besuchen können.

Die Art und Weise, wie Sie mit der Authentifizierung einen beliebigen Dienst auf API-Gatewayebene sichern, besteht darin, den AuthenticationProviderKey in den zugehörigen Einstellungen auf der configuration.jsonfestzulegen.

    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

Wenn Ocelot ausgeführt wird, wird der ReRoutes AuthenticationOptions.AuthenticationProviderKey betrachtet und überprüft, ob ein Authentifizierungsanbieter mit dem angegebenen Schlüssel registriert ist. Ist dies nicht der Fehler, wird Ocelot nicht gestartet. Wenn vorhanden, verwendet die ReRoute diesen Anbieter, wenn er ausgeführt wird.

Da der Ocelot WebHost mit dem authenticationProviderKey = "IdentityApiKey"Konfiguriert ist, ist dies immer dann Authentifizierung erforderlich, wenn dieser Dienst Anforderungen ohne Authentifizierungstoken hat.

namespace OcelotApiGw
{
    public class Startup
    {
        private readonly IConfiguration _cfg;

        public Startup(IConfiguration configuration) => _cfg = configuration;

        public void ConfigureServices(IServiceCollection services)
        {
            var identityUrl = _cfg.GetValue<string>("IdentityUrl");
            var authenticationProviderKey = "IdentityApiKey";
                         //…
            services.AddAuthentication()
                .AddJwtBearer(authenticationProviderKey, x =>
                {
                    x.Authority = identityUrl;
                    x.RequireHttpsMetadata = false;
                    x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" }
                    };
                });
            //...
        }
    }
}

Anschließend müssen Sie auch die Autorisierung mit dem [Authorize]-Attribut für jede Ressource festlegen, auf die wie die Microservices zugegriffen werden soll, z. B. im folgenden Basket Microservice-Controller.

namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
    [Route("api/v1/[controller]")]
    [Authorize]
    public class BasketController : Controller
    {
      //...
    }
}

Die ValidAudiences wie "basket" werden mit der Zielgruppe korreliert, die in jedem Microservice mit AddJwtBearer() den ConfigureServices() der Startup-Klasse definiert ist, z. B. im folgenden Code.

// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

var identityUrl = Configuration.GetValue<string>("IdentityUrl");

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "basket";
});

Wenn Sie versuchen, auf einen gesicherten Microservice zuzugreifen, z. B. den Basket microservice mit einer ReRoute-URL, die auf dem API-Gateway http://host.docker.internal:5202/api/v1/b/basket/1basiert, erhalten Sie eine "401 Nicht autorisiert", es sei denn, Sie geben ein gültiges Token an. Wenn eine ReRoute-URL authentifiziert wird, ruft Ocelot dagegen das zugehörige Downstreamschema (die interne Microservice-URL) auf.

Autorisierung auf der ReRoutes-Ebene von Ocelot. Ocelot unterstützt die anspruchsbasierte Autorisierung, die nach der Authentifizierung ausgewertet wird. Sie legen die Autorisierung auf Routenebene fest, indem Sie der ReRoute-Konfiguration die folgenden Zeilen hinzufügen.

"RouteClaimsRequirement": {
    "UserType": "employee"
}

Wenn die Autorisierungs-Middleware aufgerufen wird, wird Ocelot in diesem Beispiel feststellen, ob der Benutzer den Anspruchstyp "UserType" im Token hat und der Wert dieses Anspruchs "Employee" lautet. Andernfalls wird der Benutzer nicht autorisiert, und die Antwort ist 403 verboten.

Verwenden von Kubernetes Ingress plus Ocelot-API-Gateways

Bei Verwendung von Kubernetes (z. B. in einem Azure Kubernetes Service Cluster) vereinheitlichen Sie in der Regel alle HTTP-Anforderungen über die Kubernetes Ingress-Ebene basierend auf Nginx.

Wenn Sie in Kubernetes keinen Eingangsansatz verwenden, verfügen Ihre Dienste und Pods nur über IPs, die vom Clusternetzwerk routingfähig sind.

Wenn Sie jedoch einen Eingangsansatz verwenden, haben Sie eine mittlere Ebene zwischen dem Internet und Ihren Diensten (einschließlich Ihrer API-Gateways), die als Reverseproxy fungieren.

Als Definition ist ein Ingress eine Sammlung von Regeln, mit denen eingehende Verbindungen die Clusterdienste erreichen können. Ein Ingress ist so konfiguriert, dass Dienste extern erreichbare URLs, Lastenausgleichsdatenverkehr, SSL-Beendigung und vieles mehr bereitgestellt werden. Benutzer fordern den Eingangseingang von POSTing der Ingress-Ressource an den API-Server an.

Bei eShopOnContainers verwenden Sie bei der lokalen Entwicklung und Verwendung nur Ihres Entwicklungscomputers als Docker-Host keinen Eingang, sondern nur die mehreren API-Gateways.

Bei der Ausrichtung auf eine "Produktionsumgebung", die auf Kubernetes basiert, verwendet eShopOnContainers jedoch einen Eingangs vor den API-Gateways. Auf diese Weise rufen die Clients weiterhin dieselbe Basis-URL auf, die Anforderungen werden jedoch an mehrere API-Gateways oder BFF weitergeleitet.

API-Gateways sind Front-Ends oder Fassaden, die nur die Dienste, aber nicht die Webanwendungen darstellen, die in der Regel außerhalb ihres Gültigkeitsbereichs liegen. Darüber hinaus können die API-Gateways bestimmte interne Microservices ausblenden.

Der Ausgang leitet jedoch nur HTTP-Anforderungen um, versucht jedoch nicht, mikroservice- oder Web-App auszublenden.

Eine Eingangs-Nginx-Ebene in Kubernetes vor den Webanwendungen sowie mehrere Ocelot-API-Gateways / BFF ist die ideale Architektur, wie im folgenden Diagramm dargestellt.

Ein Diagramm, das zeigt, wie eine Eingangsebene in die AKS-Umgebung passt.

Abbildung 6-41. Die Eingangsebene in eShopOnContainers, wenn sie in Kubernetes bereitgestellt wird

Ein Kubernetes Ingress fungiert als Reverseproxy für den gesamten Datenverkehr an die App, einschließlich der Webanwendungen, die sich außerhalb des API-Gatewaybereichs befinden. Wenn Sie eShopOnContainers in Kubernetes bereitstellen, werden nur wenige Dienste oder Endpunkte über den Ingress verfügbar gemacht, im Grunde die folgende Liste der Postfixe für die URLs:

  • / für die Client-SPA-Webanwendung
  • /webmvc für die Client-MVC-Webanwendung
  • /webstatus für die Clientweb-App mit status/healthchecks
  • /webshoppingapigw für das Web BFF und Einkaufsgeschäftsprozesse
  • /webmarketingapigw für das Web BFF und Marketing Geschäftsprozesse
  • /mobileshoppingapigw für die mobilen BFF- und Einkaufsgeschäftsprozesse
  • /mobilemarketingapigw für die mobilen BFF- und Marketing-Geschäftsprozesse

Bei der Bereitstellung in Kubernetes verwendet jedes Ocelot-API-Gateway für jeden Pod , der die API-Gateways ausführt, eine andereconfiguration.json"-Datei. Diese "configuration.json"-Dateien werden von der Bereitstellung (ursprünglich mit dem deploy.ps1 Skript) einem Volume bereitgestellt, das auf einer Kubernetes-Konfigurationszuordnung namens "ocelot" erstellt wurde. Jeder Container stellt seine zugehörige Konfigurationsdatei im Ordner des Containers mit dem Namen /app/configurationbereit.

In den Quellcodedateien von eShopOnContainers finden Sie die ursprünglichen Dateien "configuration.json" im k8s/ocelot/ Ordner. Für jeden BFF/APIGateway gibt es eine Datei.

Zusätzliche cross-cutting Features in einem Ocelot-API-Gateway

Es gibt weitere wichtige Features, die sie recherchieren und verwenden können, wenn Sie ein Ocelot-API-Gateway verwenden, das in den folgenden Links beschrieben ist.