BLOG | NGINX

Erstellen von Microservices: Verwenden eines API-Gateways

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Chris Richardson Miniaturbild
Chris Richardson
Veröffentlicht am 15. Juni 2015

Redaktion – Die siebenteilige Artikelserie ist nun komplett:

  1. Einführung in Microservices
  2. Erstellen von Microservices: Verwenden eines API-Gateways (dieser Artikel)
  3. Erstellen von Microservices: Interprozesskommunikation in einer Microservices-Architektur
  4. Service Discovery in einer Microservices-Architektur
  5. Ereignisgesteuertes Datenmanagement für Microservices
  6. Auswählen einer Bereitstellungsstrategie für Microservices
  7. Refactoring eines Monolithen in Microservices

Sie können den kompletten Artikelsatz sowie Informationen zur Implementierung von Microservices mit NGINX Plus auch als E-Book herunterladen – Microservices: Vom Entwurf bis zur Bereitstellung . Schauen Sie sich auch die neue Seite mit Microservices-Lösungen an.

Der erste Artikel dieser siebenteiligen Reihe zum Entwerfen, Erstellen und Bereitstellen von Microservices stellte das Microservices-Architekturmuster vor. Dabei wurden die Vor- und Nachteile der Verwendung von Microservices erörtert und erläutert, dass Microservices trotz ihrer Komplexität in der Regel die ideale Wahl für komplexe Anwendungen darstellen. Dies ist der zweite Artikel der Reihe und befasst sich mit der Erstellung von Microservices mithilfe eines API-Gateways.

Wenn Sie Ihre Anwendung als eine Reihe von Microservices erstellen möchten, müssen Sie entscheiden, wie die Clients Ihrer Anwendung mit den Microservices interagieren. Bei einer monolithischen Anwendung gibt es nur einen Satz (normalerweise replizierter, lastausgeglichener) Endpunkte. In einer Microservices-Architektur stellt jedoch jeder Microservice einen Satz typischerweise feinkörniger Endpunkte bereit. In diesem Artikel untersuchen wir, wie sich dies auf die Kommunikation zwischen Client und Anwendung auswirkt, und schlagen einen Ansatz vor, der ein API-Gateway verwendet.

Einführung

Stellen Sie sich vor, Sie entwickeln einen nativen mobilen Client für eine Shopping-Anwendung. Wahrscheinlich müssen Sie eine Produktdetailseite implementieren, auf der Informationen zu einem bestimmten Produkt angezeigt werden.

Das folgende Diagramm zeigt beispielsweise, was Sie sehen, wenn Sie in der mobilen Android-Anwendung von Amazon durch die Produktdetails scrollen.

Indizierte Elemente der mobilen App von Amazon für Android, wie sie auf einem Mobiltelefonbildschirm erscheinen

Obwohl es sich um eine Smartphone-Anwendung handelt, werden auf der Produktdetailseite zahlreiche Informationen angezeigt. Beispielsweise werden auf dieser Seite nicht nur grundlegende Produktinformationen (wie Name, Beschreibung und Preis) angezeigt, sondern auch:

  • Anzahl der Artikel im Warenkorb
  • Bestellverlauf
  • Kundenrezensionen
  • Warnung „Niedriger Lagerbestand“
  • Versandoptionen
  • Verschiedene Empfehlungen, darunter andere Produkte, mit denen dieses Produkt häufig zusammen gekauft wurde, andere Produkte, die von Kunden gekauft wurden, die dieses Produkt gekauft haben, und andere Produkte, die von Kunden angesehen wurden, die dieses Produkt gekauft haben
  • Alternative Kaufoptionen

Bei Verwendung einer monolithischen Anwendungsarchitektur würde ein mobiler Client diese Daten abrufen, indem er einen einzelnen REST-Aufruf ( GET api.company.com/productdetails/ productId ) an die Anwendung sendet. Ein Load Balancer leitet die Anfrage an eine von N identischen Anwendungsinstanzen weiter. Die Anwendung würde dann verschiedene Datenbanktabellen abfragen und die Antwort an den Client zurückgeben.

Im Gegensatz dazu sind bei Verwendung der Microservices-Architektur die auf der Produktdetailseite angezeigten Daten Eigentum mehrerer Microservices. Dies sind einige der potenziellen Microservices, denen die auf der Beispiel-Produktdetailseite angezeigten Daten gehören:

  • Warenkorbservice – Anzahl der Artikel im Warenkorb
  • Bestellservice – Bestellverlauf
  • Katalogdienst – Grundlegende Produktinformationen wie Name, Bild und Preis
  • Bewertungsservice – Kundenrezensionen
  • Inventardienst – Warnung bei niedrigem Lagerbestand
  • Versandservice – Versandoptionen, -fristen und -kosten werden separat über die API des Versandanbieters abgerufen
  • Empfehlungsdienst(e) – Vorgeschlagene Artikel

Der mobile Client einer E-Commerce-App benötigt eine Möglichkeit, auf die RESTful-APIs der 7 Microservices zuzugreifen

Wir müssen entscheiden, wie der mobile Client auf diese Dienste zugreift. Schauen wir uns die Optionen an.

Direkte Client-zu-Microservice-Kommunikation

Theoretisch könnte ein Client direkt an jeden der Microservices Anfragen stellen. Jeder Mikrodienst hätte einen öffentlichen Endpunkt ( https:// serviceName .api.company.name ). Diese URL würde dem Load Balancer des Microservices entsprechen, der die Anfragen auf die verfügbaren Instanzen verteilt. Um die Produktdetails abzurufen, würde der mobile Client Anfragen an jeden der oben aufgeführten Dienste stellen.

Leider bringt diese Option Herausforderungen und Einschränkungen mit sich. Ein Problem besteht darin, dass die Anforderungen des Clients nicht mit den detaillierten APIs übereinstimmen, die von den einzelnen Microservices bereitgestellt werden. Der Client in diesem Beispiel muss sieben separate Anfragen stellen. Bei komplexeren Anwendungen müssen möglicherweise deutlich mehr davon erstellt werden. Amazon beschreibt beispielsweise, wie an der Darstellung seiner Produktseite Hunderte von Diensten beteiligt sind. Während ein Client über ein LAN so viele Anfragen stellen könnte, wäre dies über das öffentliche Internet wahrscheinlich zu ineffizient und über ein Mobilfunknetz definitiv unpraktisch. Dieser Ansatz macht den Clientcode auch wesentlich komplexer.

Ein weiteres Problem beim direkten Aufrufen der Microservices durch den Client besteht darin, dass einige möglicherweise Protokolle verwenden, die nicht webfreundlich sind. Ein Dienst verwendet möglicherweise Thrift Binary RPC, während ein anderer Dienst das AMQP-Messaging-Protokoll verwendet. Keines der Protokolle ist besonders browser- oder Firewall-freundlich und wird am besten intern verwendet. Eine Anwendung sollte außerhalb der Firewall Protokolle wie HTTP und WebSocket verwenden.

Ein weiterer Nachteil dieses Ansatzes besteht darin, dass er die Refaktorierung der Microservices erschwert. Mit der Zeit möchten wir möglicherweise die Aufteilung des Systems in Dienste ändern. Beispielsweise könnten wir zwei Dienste zusammenführen oder einen Dienst in zwei oder mehr Dienste aufteilen. Wenn Clients jedoch direkt mit den Diensten kommunizieren, kann die Durchführung dieser Art von Refactoring äußerst schwierig sein.

Aufgrund dieser Art von Problemen ist es für Clients selten sinnvoll, direkt mit Microservices zu kommunizieren.

Verwenden eines API-Gateways

Normalerweise ist die Verwendung eines sogenannten API-Gateways ein viel besserer Ansatz. Ein API-Gateway ist ein Server, der den einzigen Einstiegspunkt in das System darstellt. Es ähnelt dem Facade- Muster aus dem objektorientierten Design. Das API-Gateway kapselt die interne Systemarchitektur und stellt für jeden Client eine maßgeschneiderte API bereit. Es kann weitere Aufgaben übernehmen, beispielsweise Authentifizierung, Überwachung, Lastenausgleich, Zwischenspeicherung, Anforderungsgestaltung und -verwaltung sowie die Verarbeitung statischer Antworten.

Das folgende Diagramm zeigt, wie ein API-Gateway normalerweise in die Architektur passt:

Ein API-Gateway ermöglicht mobilen Clients einer E-Commerce-App den Zugriff auf die RESTful-APIs ihrer 7 Microservices

Das API-Gateway ist für die Anforderungsweiterleitung, -zusammenstellung und Protokollübersetzung verantwortlich. Alle Anfragen von Clients gehen zunächst durch das API-Gateway. Anschließend leitet es die Anfragen an den entsprechenden Mikrodienst weiter. Das API-Gateway verarbeitet eine Anfrage häufig, indem es mehrere Mikrodienste aufruft und die Ergebnisse aggregiert. Es kann zwischen Webprotokollen wie HTTP und WebSocket und intern verwendeten webunfreundlichen Protokollen übersetzen.

Das API-Gateway kann jedem Client auch eine benutzerdefinierte API bereitstellen. Normalerweise stellt es eine grobkörnige API für mobile Clients bereit. Betrachten Sie beispielsweise das Szenario mit den Produktdetails. Das API-Gateway kann einen Endpunkt ( /productdetails?productid= xxx ) bereitstellen, der es einem mobilen Client ermöglicht, alle Produktdetails mit einer einzigen Anfrage abzurufen. Das API-Gateway verarbeitet die Anfrage, indem es die verschiedenen Dienste – Produktinformationen, Empfehlungen, Bewertungen usw. – aufruft und die Ergebnisse kombiniert.

Ein großartiges Beispiel für ein API-Gateway ist das Netflix API Gateway . Der Netflix-Streaming-Dienst ist auf Hunderten verschiedener Gerätetypen verfügbar, darunter Fernseher, Set-Top-Boxen, Smartphones, Spielsysteme, Tablets usw. Ursprünglich versuchte Netflix, eine einheitliche API für seinen Streaming-Dienst bereitzustellen. Sie stellten jedoch fest, dass dies aufgrund der großen Vielfalt an Geräten und deren unterschiedlichen Anforderungen nicht gut funktionierte. Heute verwenden sie ein API-Gateway, das durch die Ausführung gerätespezifischen Adaptercodes eine auf jedes Gerät zugeschnittene API bereitstellt. Ein Adapter verarbeitet normalerweise jede Anforderung, indem er durchschnittlich sechs bis sieben Backend-Dienste aufruft. Das Netflix API Gateway verarbeitet täglich Milliarden von Anfragen.

Vorteile und Nachteile eines API-Gateways

Wie zu erwarten, hat die Verwendung eines API-Gateways sowohl Vor- als auch Nachteile. Ein großer Vorteil der Verwendung eines API-Gateways besteht darin, dass es die interne Struktur der Anwendung kapselt. Anstatt bestimmte Dienste aufrufen zu müssen, kommunizieren Clients einfach mit dem Gateway. Das API-Gateway stellt für jeden Clienttyp eine bestimmte API bereit. Dadurch wird die Anzahl der Roundtrips zwischen Client und Anwendung reduziert. Außerdem wird dadurch der Clientcode vereinfacht.

Das API-Gateway hat auch einige Nachteile. Es handelt sich um eine weitere hochverfügbare Komponente, die entwickelt, bereitgestellt und verwaltet werden muss. Darüber hinaus besteht das Risiko, dass das API-Gateway zu einem Entwicklungsengpass wird. Entwickler müssen das API-Gateway aktualisieren, um die Endpunkte jedes Mikrodienstes verfügbar zu machen. Es ist wichtig, dass der Prozess zum Aktualisieren des API-Gateways so einfach wie möglich ist. Andernfalls müssen Entwickler in der Warteschlange warten, um das Gateway zu aktualisieren. Trotz dieser Nachteile ist es für die meisten realen Anwendungen sinnvoll, ein API-Gateway zu verwenden.

Implementieren eines API-Gateways

Nachdem wir uns nun die Beweggründe und Nachteile der Verwendung eines API-Gateways angesehen haben, schauen wir uns nun die verschiedenen Designaspekte an, die Sie berücksichtigen müssen.

Leistung und Skalierbarkeit

Nur eine Handvoll Unternehmen operieren in der Größenordnung von Netflix und müssen täglich Milliarden von Anfragen verarbeiten. Für die meisten Anwendungen ist jedoch die Leistung und Skalierbarkeit des API-Gateways normalerweise sehr wichtig. Daher ist es sinnvoll, das API-Gateway auf einer Plattform zu erstellen, die asynchronen, nicht blockierenden E/A unterstützt. Es gibt eine Vielzahl unterschiedlicher Technologien, die zur Implementierung eines skalierbaren API-Gateways verwendet werden können. Auf der JVM können Sie eines der NIO-basierten Frameworks wie Netty, Vertx, Spring Reactor oder JBoss Undertow verwenden. Eine beliebte Nicht-JVM-Option ist Node.js, eine Plattform, die auf der JavaScript-Engine von Chrome basiert. Eine andere Möglichkeit ist die Verwendung von NGINX Plus . NGINX Plus bietet einen ausgereiften, skalierbaren, leistungsstarken Webserver und Reverse-Proxy, der einfach bereitgestellt, konfiguriert und programmiert werden kann. NGINX Plus kann Authentifizierung, Zugriffskontrolle, Lastausgleichsanforderungen und Caching-Antworten verwalten und bietet anwendungsbezogene Integritätsprüfungen und Überwachung.

Verwenden eines reaktiven Programmiermodells

Das API-Gateway verarbeitet einige Anfragen, indem es sie einfach an den entsprechenden Backend-Dienst weiterleitet. Es verarbeitet andere Anfragen, indem es mehrere Backend-Dienste aufruft und die Ergebnisse aggregiert. Bei manchen Anfragen, etwa einer Produktdetailseite, sind die Anfragen an die Backend-Dienste voneinander unabhängig. Um die Antwortzeit zu minimieren, sollte das API-Gateway unabhängige Anfragen gleichzeitig ausführen. Manchmal bestehen allerdings Abhängigkeiten zwischen Anfragen. Das API-Gateway muss die Anforderung möglicherweise zuerst durch Aufrufen eines Authentifizierungsdienstes validieren, bevor die Anforderung an einen Back-End-Dienst weitergeleitet wird. Um Informationen zu den Produkten auf der Wunschliste eines Kunden abzurufen, muss das API-Gateway zunächst das Kundenprofil mit diesen Informationen abrufen und dann die Informationen für jedes Produkt abrufen. Ein weiteres interessantes Beispiel für API-Komposition ist das Netflix Video Grid .

Das Schreiben von API-Kompositionscode mit dem herkömmlichen asynchronen Callback-Ansatz führt Sie schnell in die Callback-Hölle. Der Code wird kompliziert, schwer verständlich und fehleranfällig sein. Ein viel besserer Ansatz besteht darin, API Gateway-Code in einem deklarativen Stil mit einem reaktiven Ansatz zu schreiben. Beispiele für reaktive Abstraktionen sind Future in Scala, CompletableFuture in Java 8 und Promise in JavaScript. Es gibt auch Reactive Extensions (auch Rx oder ReactiveX genannt), das ursprünglich von Microsoft für die .NET-Plattform entwickelt wurde. Netflix hat RxJava für die JVM speziell zur Verwendung in seinem API-Gateway erstellt. Es gibt auch RxJS für JavaScript, das sowohl im Browser als auch in Node.js läuft. Durch die Verwendung eines reaktiven Ansatzes können Sie einfachen und dennoch effizienten API-Gateway-Code schreiben.

Dienstaufruf

Eine auf Microservices basierende Anwendung ist ein verteiltes System und muss einen Interprozesskommunikationsmechanismus verwenden. Es gibt zwei Arten der Interprozesskommunikation. Eine Möglichkeit besteht darin, einen asynchronen, auf Nachrichten basierenden Mechanismus zu verwenden. Einige Implementierungen verwenden einen Nachrichtenbroker wie JMS oder AMQP. Andere, wie beispielsweise Zeromq, kommen ohne Broker aus und die Dienste kommunizieren direkt miteinander. Die andere Art der Interprozesskommunikation ist ein synchroner Mechanismus wie HTTP oder Thrift. Ein System verwendet normalerweise sowohl asynchrone als auch synchrone Stile. Es könnten sogar mehrere Implementierungen jedes Stils verwendet werden. Folglich muss das API-Gateway verschiedene Kommunikationsmechanismen unterstützen.

Diensterkennung

Das API-Gateway muss den Standort (IP-Adresse und Port) jedes Mikrodienstes kennen, mit dem es kommuniziert. In einer herkömmlichen Anwendung könnten Sie die Standorte wahrscheinlich fest verdrahten, aber in einer modernen, Cloud-basierten Microservices-Anwendung ist dies ein nicht triviales Problem. Infrastrukturdienste wie ein Nachrichtenbroker haben normalerweise einen statischen Standort, der über Umgebungsvariablen des Betriebssystems angegeben werden kann. Allerdings ist es nicht so einfach, den Standort eines Anwendungsdienstes zu bestimmen. Anwendungsdiensten werden Standorte dynamisch zugewiesen. Außerdem ändert sich die Anzahl der Instanzen eines Dienstes aufgrund automatischer Skalierung und Upgrades dynamisch. Folglich muss das API-Gateway wie jeder andere Service-Client im System den Service-Erkennungsmechanismus des Systems verwenden: entweder Server-Side Discovery oder Client-Side Discovery . In einem späteren Artikel wird die Diensterkennung ausführlicher beschrieben. Vorerst ist es wichtig zu beachten, dass, wenn das System Client‑Side Discovery verwendet, das API‑Gateway in der Lage sein muss, das Service Registry abzufragen. Dabei handelt es sich um eine Datenbank aller Microservice‑Instanzen und ihrer Standorte.

Teilfehler behandeln

Ein weiteres Problem, das Sie bei der Implementierung eines API-Gateways berücksichtigen müssen, ist das Problem teilweiser Fehler. Dieses Problem tritt in allen verteilten Systemen auf, wenn ein Dienst einen anderen Dienst aufruft, der entweder langsam reagiert oder nicht verfügbar ist. Das API-Gateway sollte niemals unbegrenzt blockiert sein, während es auf einen nachgelagerten Dienst wartet. Die Vorgehensweise beim Fehlerumgang hängt jedoch vom jeweiligen Szenario und dem fehlgeschlagenen Dienst ab. Wenn beispielsweise der Empfehlungsdienst im Produktdetailszenario nicht reagiert, sollte das API-Gateway die restlichen Produktdetails an den Client zurückgeben, da diese für den Nutzer weiterhin nützlich sind. Die Empfehlungen könnten entweder leer sein oder beispielsweise durch eine fest programmierte Top-Ten-Liste ersetzt werden. Wenn der Produktinformationsdienst jedoch nicht reagiert, sollte das API Gateway einen Fehler an den Client zurückgeben.

Das API-Gateway könnte auch zwischengespeicherte Daten zurückgeben, wenn diese verfügbar wären. Da sich beispielsweise die Produktpreise selten ändern, könnte das API-Gateway zwischengespeicherte Preisdaten zurückgeben, wenn der Preisdienst nicht verfügbar ist. Die Daten können vom API-Gateway selbst zwischengespeichert oder in einem externen Cache wie Redis oder Memcached gespeichert werden. Indem entweder Standarddaten oder zwischengespeicherte Daten zurückgegeben werden, stellt das API-Gateway sicher, dass sich Systemausfälle nicht auf das Nutzererlebnis auswirken.

Netflix Hystrix ist eine unglaublich nützliche Bibliothek zum Schreiben von Code, der Remotedienste aufruft. Bei Anrufen, die den angegebenen Schwellenwert überschreiten, wird von Hystrix eine Zeitüberschreitung festgestellt. Es implementiert ein Leistungsschaltermuster , das verhindert, dass der Client unnötig auf einen nicht reagierenden Dienst wartet. Wenn die Fehlerrate für einen Dienst einen bestimmten Schwellenwert überschreitet, löst Hystrix den Leistungsschalter aus und alle Anforderungen schlagen für einen bestimmten Zeitraum sofort fehl. Mit Hystrix können Sie eine Fallback-Aktion definieren, wenn eine Anforderung fehlschlägt, z. B. das Lesen aus einem Cache oder die Rückgabe eines Standardwerts. Wenn Sie die JVM verwenden, sollten Sie unbedingt die Verwendung von Hystrix in Betracht ziehen. Und wenn Sie in einer Nicht-JVM-Umgebung arbeiten, sollten Sie eine entsprechende Bibliothek verwenden.

Zusammenfassung

Für die meisten auf Microservices basierenden Anwendungen ist die Implementierung eines API-Gateways sinnvoll, das als einziger Einstiegspunkt in ein System fungiert. Das API-Gateway ist für die Anforderungsweiterleitung, -zusammenstellung und Protokollübersetzung verantwortlich. Es stellt jedem Client der Anwendung eine benutzerdefinierte API bereit. Das API-Gateway kann auch Fehler in den Back-End-Diensten maskieren, indem es zwischengespeicherte oder Standarddaten zurückgibt. Im nächsten Artikel der Serie werden wir uns mit der Kommunikation zwischen Diensten befassen.

Redaktion – Die siebenteilige Artikelserie ist nun komplett:

  1. Einführung in Microservices
  2. Erstellen von Microservices: Verwenden eines API-Gateways (dieser Artikel)
  3. Erstellen von Microservices: Interprozesskommunikation in einer Microservices-Architektur
  4. Service Discovery in einer Microservices-Architektur
  5. Ereignisgesteuertes Datenmanagement für Microservices
  6. Auswählen einer Bereitstellungsstrategie für Microservices
  7. Refactoring eines Monolithen in Microservices

Sie können den kompletten Artikelsatz sowie Informationen zur Implementierung von Microservices mit NGINX Plus auch als E-Book herunterladen – Microservices: Vom Entwurf bis zur Bereitstellung .

Einen detaillierten Blick auf weitere Anwendungsfälle werfen Sie in unsere dreiteilige Blogserie „NGINX Plus als API-Gateway bereitstellen“ :

  • Teil 1 bietet detaillierte Konfigurationsanweisungen für mehrere Anwendungsfälle.
  • Teil 2 erweitert diese Anwendungsfälle und befasst sich mit einer Reihe von Sicherheitsvorkehrungen, die zum Schutz und zur Sicherung von Back-End-API-Diensten in der Produktion angewendet werden können.
  • Teil 3 erläutert, wie NGINX Plus als API-Gateway für gRPC-Dienste bereitgestellt wird.

Gastblogger Chris Richardson ist der Gründer des ursprünglichen CloudFoundry.com , einer frühen Java PaaS (Platform as a Service) für Amazon EC2. Heute berät er Organisationen bei der Verbesserung der Entwicklung und Bereitstellung von Anwendungen. Unter http://microservices.io bloggt er außerdem regelmäßig über Microservices.


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."