Anmerkung des Autors – Dieser Blogbeitrag ist der sechste in einer Reihe:
Alle sechs Blogs sowie ein Blog über Web-Frontends für Microservices-Anwendungen<.htmla> wurden in einem kostenlosen E-Book zusammengefasst.
Schauen Sie sich auch diese anderen NGINX-Ressourcen zu Microservices an:
Das Design von Microservices-Anwendungen hat die Funktionsweise von Anwendungen grundlegend verändert. In einer Microservices-Architektur ist eine „Anwendung“ nun eine Sammlung von Diensten, die voneinander abhängig sind, um Aufgaben auszuführen und Funktionen bereitzustellen. Bei komplexen Anwendungen kann das Servicediagramm recht umfangreich sein und zahlreiche Abhängigkeiten zwischen den verschiedenen Services aufweisen.
Beispielsweise kann ein Benutzerdienst integraler Bestandteil vieler anderer Dienste sein, die auf die vom Dienst bereitgestellten Daten angewiesen sind. In diesem Szenario kann ein Ausfall des Benutzerdienstes eine Kaskade von Fehlern in der gesamten Anwendung auslösen.
Das Circuit-Breaker-Muster – ein von Martin Fowler populär gemachter Begriff – gewinnt unter Microservices-Architekten als Anwendungsdesignmuster zur Vermeidung kaskadierender Serviceausfälle an Bedeutung. Die Idee des Circuit-Breaker-Musters besteht darin, Ihre Anwendungsdienste und den zwischen ihnen fließenden Datenverkehr zu überwachen, um Ausfällen vorzubeugen – und, falls es zu Ausfällen kommt, die Auswirkungen dieser Ausfälle auf Ihre Anwendungen zu minimieren.
Für Mikroservices ist das Circuit-Breaker-Muster besonders wertvoll, da es eine Bottom-Up-Ausfallsicherheit bietet. Bei richtiger Implementierung kann es dazu beitragen, kaskadierende Ausfälle zu vermeiden, indem es die Dienstkontinuität auch dann gewährleistet, wenn Dienste nicht verfügbar sind. Das Circuit-Breaker-Muster wurde vor allem von Netflix als kritischer Bestandteil seiner Anwendungsdesignphilosophie übernommen .
Ein zentraler Grundsatz des modernen Anwendungsdesigns besteht darin, dass Fehler auftreten werden. Der vielschichtige Kuchen, auf den sich moderne Anwendungen stützen – von in der Cloud gehosteten virtuellen Maschinen über Container und Anwendungsbibliotheken bis hin zum dynamischen Netzwerkbetrieb – bedeutet, dass es in jeder Anwendung unzählige bewegliche Teile gibt. Sie müssen davon ausgehen, dass ein oder mehrere Teile Ihrer Anwendung irgendwann auf die eine oder andere Weise fehlschlagen werden. Wenn Sie mit Fehlern rechnen und Mechanismen zur Milderung ihrer Auswirkungen einbauen, tragen Sie wesentlich dazu bei, Ihre Anwendung widerstandsfähiger zu machen.
Eines der wichtigsten Ziele des Leistungsschaltermusters besteht darin, Ausfälle von vornherein zu verhindern. Bei manchen Fehlerzuständen, etwa wenn nicht genügend Arbeitsspeicher zur Verfügung steht, ist es möglich, einen bevorstehenden Ausfall zu erkennen und Maßnahmen zu ergreifen, um ihn zu verhindern. Dies wird normalerweise dadurch erreicht, dass der Dienst signalisiert, dass er nicht ordnungsgemäß funktioniert. Der Leistungsschalter gibt dem Dienst dann die Chance zur Wiederherstellung, indem er die Anzahl der Anfragen drosselt oder sie vollständig umleitet. Sobald der Dienst wiederhergestellt ist, ist es für den Leistungsschalter außerdem ratsam, die Anfragen an den Dienst langsam zu steigern, um ihn nicht sofort zu überlasten und das Risiko einzugehen, dass er erneut ausfällt.
In der NGINX Microservices Reference Architecture haben wir einen Dienst namens Resizer . Wenn ein großes Foto auf das System hochgeladen wird, dekomprimiert der Resizer das Foto, korrigiert seine Drehung, verkleinert es und verkleinert es dann erneut. Dabei werden das korrigierte Originalbild und die beiden skalierten Bilder in einem Objektspeicher gespeichert. Aufgrund der Art dieser Prozesse ist die Größenanpassung der prozessor- und speicherintensivste Teil der Anwendung.
Wenn die Größe vieler Bilder gleichzeitig geändert wird, kann es sein, dass dem Größenanpasser nicht genügend Speicher zur Verfügung steht und er in manchen Fällen vollständig fehlschlägt. Um Probleme zu vermeiden, platzieren wir einen Leistungsschalter zwischen den Instanzen des Resizer-Dienstes und dem Uploader -Dienst, der ihnen Bilder zuführt. Der Uploader fragt die Resizer-Instanzen regelmäßig nach ihrem Integritätsstatus ab. Die Abfrage veranlasst den Resizer, neben anderen Integritätsprüfungen zu prüfen, ob er mehr als 80 % des verfügbaren Speichers verwendet hat, und antwortet dem Uploader mit seinem Integritätsstatus.
Wenn eine Resizer-Instanz anzeigt, dass sie nicht in Ordnung ist, leitet der Uploader Anfragen an andere Instanzen weiter – wie in Abbildung 1 gezeigt –, prüft aber weiterhin, ob diese Resizer-Instanz wiederhergestellt wurde. Wenn die Resizer-Instanz anzeigt, dass sie wieder fehlerfrei ist, wird sie zurück in den Pool mit Lastenausgleich gelegt und der Uploader steigert den Datenverkehr langsam bis zur vollen Kapazität der Instanz. Dieses Design verhindert, dass Instanzen des Resizers vollständig fehlschlagen, dass Arbeiten begonnen, aber nicht abgeschlossen werden, dass Benutzer, deren Prozesse andernfalls fehlgeschlagen wären, übermäßig lange warten müssen und hilft dem System dabei, den an es gesendeten Anforderungsstrom möglichst effektiv zu verarbeiten.
Einer der Vorteile der Implementierung des Leistungsschalters auf NGINX-Ebene besteht darin, dass dadurch eine universelle, konsistente und äußerst flexible Ebene für die Verwaltung von Leistungsschaltern in Ihrer gesamten Microservices-Anwendung erstellt wird. Diese Universalität und Konsistenz bedeutet, dass Sie die Nuancen und Inkonsistenzen der Circuit-Breaker-Bibliotheken für jede Sprache nicht verwalten und berücksichtigen müssen.
Wenn Sie die Circuit-Breaker-Funktionalität größtenteils aus dem Code der einzelnen Dienste heraushalten und stattdessen in NGINX Plus implementieren, erzielen Sie zahlreiche Vorteile:
Es ist jedoch wichtig zu beachten, dass Leistungsschalter nicht allein in NGINX Plus implementiert werden können. Ein echter Leistungsschalter erfordert, dass der Dienst eine introspektive, aktive Integritätsprüfung unter einer angegebenen URI (normalerweise /health ) bereitstellt. Der Gesundheitscheck muss den Anforderungen des jeweiligen Dienstes angemessen sein.
Beim Entwickeln der Integritätsprüfung müssen Sie das Fehlerprofil des Dienstes und die Arten von Bedingungen verstehen, die zu einem Fehler führen können, z. B. ein Datenbankverbindungsfehler, unzureichender Arbeitsspeicher, zu wenig Speicherplatz oder eine überlastete CPU. Diese Bedingungen werden im Integritätsprüfprozess ausgewertet, der dann einen binären Status „integriert“ oder „ungesund“ liefert.
Wenn Sie das Circuit-Breaker-Muster wie hier beschrieben auf der NGINX-Ebene implementieren, muss NGINX Plus die Situation bewältigen, wenn eine Serviceinstanz einen Fehler meldet. Es gibt mehrere Möglichkeiten.
Die erste Möglichkeit besteht darin, die Anfragen an andere, fehlerfreie Instanzen umzuleiten und weiterhin bei der fehlerhaften Instanz nachzufragen, um zu sehen, ob sie sich erholt. Die zweite Möglichkeit besteht darin, Clients, die den Dienst anfordern, zwischengespeicherte Antworten bereitzustellen. So wird die Stabilität auch dann aufrechterhalten, wenn der Dienst nicht verfügbar ist. Diese Lösung funktioniert gut mit leseorientierten Diensten, beispielsweise einem Inhaltsdienst.
Eine weitere Möglichkeit besteht darin, alternative Datenquellen bereitzustellen. Einer unserer Kunden verfügt beispielsweise über einen personalisierten Anzeigenserver, der Profildaten verwendet, um seinen Benutzern gezielte Anzeigen bereitzustellen. Wenn der Server für personalisierte Anzeigen ausfällt, wird die Benutzeranforderung an einen Backup-Server umgeleitet, der einen allgemeinen Satz für alle geeigneten Anzeigen bereitstellt. Dieser alternative Datenquellenansatz kann sehr leistungsstark sein.
Wenn Sie das Fehlerprofil eines Dienstes genau kennen, können Sie die Fehlerwahrscheinlichkeit verringern, indem Sie dem Leistungsschalter eine Ratenbegrenzung hinzufügen. Anfragen werden nur in der Rate an den Dienst weitergeleitet, die dieser verarbeiten kann. Dadurch wird innerhalb des Leistungsschalters ein Puffer erstellt, der Verkehrsspitzen absorbieren kann.
Die Ratenbegrenzung kann in einem zentralisierten Lastausgleichsszenario wie dem Router-Mesh-Modell besonders wirkungsvoll sein, bei dem der Anwendungsverkehr über eine begrenzte Anzahl von Lastverteilern geleitet wird, die sich einen guten Überblick über die gesamte Verkehrsnutzung der Site verschaffen können.
Wie oben beschrieben, kann das Circuit-Breaker-Muster Fehler verhindern, bevor sie auftreten, indem es den Datenverkehr zu einem fehlerhaften Dienst reduziert oder Anfragen von diesem wegleitet. Dies erfordert eine aktive Integritätsprüfung, die mit einem introspektiven Integritätsmonitor für jeden Dienst verbunden ist. Leider reicht ein passiver Integritätscheck nicht aus, da er lediglich auf Fehler prüft. Zu diesem Zeitpunkt ist es für vorbeugende Maßnahmen bereits zu spät. Aus diesem Grund kann NGINX Open Source das Circuit-Breaker-Muster nicht implementieren – es unterstützt nur passive Integritätsprüfungen.
NGINX Plus verfügt jedoch über ein robustes aktives Integritätsprüfsystem mit vielen Optionen zum Prüfen und Reagieren auf Integritätsprobleme. Ein Blick auf die Implementierung einiger Diensttypen für die Microservices Reference Architecture liefert gute Beispiele für die Optionen und Anwendungsfälle zur Implementierung des Leistungsschalters.
Beginnen wir mit dem Uploader-Dienst, der eine Verbindung zum Resizer herstellt. Der Uploader legt Bilder in einen Objektspeicher ab und weist dann den Größenanpasser an, ein Bild zu öffnen, es zu korrigieren und die Größe anzupassen. Dies ist ein rechen- und speicherintensiver Vorgang. Der Uploader muss den Zustand des Resizers überwachen und eine Überlastung vermeiden, da der Resizer den Host, auf dem er ausgeführt wird, buchstäblich zerstören kann.
Als Erstes müssen Sie einen Standortblock
speziell für die Resizer-Integritätsprüfung erstellen. Bei diesem Block handelt es sich um einen internen
Speicherort. Dies bedeutet, dass auf ihn nicht mit einer Anfrage an die Standard-URL des Servers ( http://example.com/health-check-resizer ) zugegriffen werden kann. Stattdessen fungiert es als Platzhalter für die Integritätsprüfungsinformationen. Die Direktive health_check
sendet alle drei Sekunden einen Integritätscheck an die URI /health und verwendet die im Match
-Block definierten Tests namens „Conditions“ , um den Integritätscheck der Serviceinstanz durchzuführen. Eine Dienstinstanz wird als fehlerhaft markiert, wenn eine einzige Prüfung fehlschlägt. Die Proxy_*-
Direktiven senden den Integritätscheck an die Resizer- Upstream-Gruppe und verwenden dabei TLS 1.2 über HTTP 1.1, wobei die angegebenen HTTP-Header auf Null gesetzt sind.
Standort /health-check-resizer { intern;
health_check uri=/health match=conditions fails=1 interval=3s;
proxy_pass https://resizer;
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.2;
proxy_http_version 1.1;
proxy_set_header Verbindung "";
proxy_set_header Accept-Encoding "";
}
Der nächste Schritt besteht darin, den Bedingungen- Übereinstimmungsblock
zu erstellen, um die Antworten anzugeben, die gesunde und ungesunde Bedingungen darstellen. Zunächst wird der Statuscode der Antwort überprüft: Liegt er im Bereich von200
durch399
, fährt der Test mit der nächsten Bewertungsanweisung fort. Die zweite Prüfung besteht darin, dass der Inhaltstyp
application/json
ist. Die dritte Prüfung besteht schließlich aus einem Abgleich regulärer Ausdrücke mit den Werten der Metriken „Deadlocks“
, „Datenträger“
und „Speicher“
. Wenn alle fehlerfrei sind, wird davon ausgegangen, dass der Dienst fehlerfrei ist.
Übereinstimmungsbedingungen { Status 200-399;
Header Inhaltstyp ~ „application/json“;
Text ~ ‚{
„Deadlocks“:{„healthy“:true},
„Disk“:{„healthy“:true},
„Memory“:{„healthy“:true}
}‘;
}
Das Leistungsschalter-/Integritätsprüfungssystem von NGINX Plus verfügt auch über eine Slow-Start-Funktion. Der Parameter „slow_start“
der Serverdirektive
für den Resizer-Dienst im Upstream
-Block weist NGINX Plus an, den Datenverkehr zu moderieren, wenn eine Resizer-Instanz zum ersten Mal aus einem fehlerhaften Zustand zurückkehrt. Anstatt den Dienst einfach mit der gleichen Anzahl von Anfragen zu belasten, die an die intakten Dienste gesendet werden, wird der Datenverkehr zum sich erholenden Dienst über den durch den Parameter „slow_start“
angegebenen Zeitraum – in diesem Fall 30 Sekunden – langsam auf die normale Geschwindigkeit hochgefahren. Durch den langsamen Start erhöhen sich die Chancen, dass der Dienst seine volle Leistungsfähigkeit wiedererlangt, und die Auswirkungen werden verringert, wenn dies nicht geschieht.
Upstream-Resizer { Server-Resizer langsamer Start = 30 s;
Zone Backend 64 k;
Least_Time Last_Byte;
Keepalive 300;
}
Durch die Anforderungsbegrenzung wird der Anforderungsfluss an den Dienst verwaltet und moderiert. Wenn Sie das Fehlerprofil der Anwendung gut genug verstehen, um zu wissen, wie viele Anfragen sie zu einem bestimmten Zeitpunkt verarbeiten kann, kann die Implementierung einer Anforderungsbegrenzung für den Prozess eine echte Erleichterung sein. Diese Funktion funktioniert jedoch nur, wenn NGINX Plus über die Gesamtzahl der an den Dienst weitergeleiteten Verbindungen genau informiert ist. Aus diesem Grund ist es am sinnvollsten, den Anforderungsbegrenzungs-Leistungsschalter auf einer NGINX Plus-Instanz zu implementieren, die in einem Container mit dem Dienst selbst ausgeführt wird (wie im Fabric-Modell) oder in einem zentralen Load Balancer, der für die Verwaltung des gesamten Datenverkehrs in einem Cluster zuständig ist.
Der folgende Konfigurationscodeausschnitt definiert eine Ratenbegrenzung für Anforderungen , die auf die Resizer-Dienstinstanzen in ihren Containern angewendet werden sollen. Die Direktive „limit_req_zone“
definiert die Ratenbegrenzung auf 100 Anfragen pro Sekunde. Die Variable „$server_addr“
wird als Schlüssel verwendet, was bedeutet, dass alle Anfragen an den Resizer-Container auf das Limit angerechnet werden. Der Name der Zone lautet „moderateReqs“ und der Zeitrahmen für die Speicherung der Anforderungsanzahl beträgt 1 Minute. Die Direktive „limit_req“
ermöglicht es NGINX Plus, Bursts von bis zu 150 Anfragen zu puffern. Wenn diese Zahl überschritten wird, erhalten die Kunden die503
Fehlercode, wie in der Direktive „limit_req_status“
angegeben, der angibt, dass der Dienst nicht verfügbar ist.
http { # Moderierte Zustellung
limit_req_zone $server_addr zone=moderateReqs:1m rate=100r/s;
# ...
server {
# ...
limit_req zone=moderateReqs burst=150;
limit_req_status 503;
# ...
}
}
Ein weiterer großer Vorteil der Ausführung des Leistungsschalters innerhalb von NGINX Plus ist die Möglichkeit, Caching zu integrieren und zwischengespeicherte Daten zentral für die systemweite Verwendung zu verwalten. Dies ist besonders wertvoll für leseorientierte Dienste wie Inhaltsserver, bei denen sich die vom Backend gelesenen Daten nicht häufig ändern.
Proxy-Cache-Pfad /app/cache Ebenen = 1:2 Schlüsselzone = Oauth-Cache: 10 m max. Größe = 10 m inaktiv = 15 s Use-Temp-Pfad = aus;
Upstream-Benutzermanager {
Server-Benutzermanager;
Zone Backend 64 kb;
Mindestzeit letztes Byte;
Keepalive 300;
}
Server {
Listen 443 SSL;
Standort /v1/Benutzer {
Proxy-Passwort http://Benutzermanager;
Proxy-Cache Oauth-Cache;
Proxy-Cache gültig 200 30 s;
Proxy-Cache verwenden veraltet Fehler Zeitüberschreitung ungültiger Header Aktualisierung
http_500 http_502 http_503 http_504;
}
}
Wie in Abbildung 2 dargestellt, führt das Zwischenspeichern von Daten dazu, dass viele Kundendatenanfragen die Microservice-Instanzen nie erreichen. Dadurch wird Kapazität für Anfragen freigegeben, die vorher nicht empfangen wurden.
Bei einem Dienst, bei dem sich Daten ändern können, beispielsweise einem Benutzerverwaltungsdienst, muss der Cache jedoch sorgfältig verwaltet werden. Andernfalls kann es passieren, dass ein Benutzer eine Änderung an seinem Profil vornimmt, in manchen Kontexten jedoch alte Daten sieht, weil die Daten zwischengespeichert sind. Dieses Problem lässt sich durch ein angemessenes Timeout und die Akzeptanz des Prinzips hoher Verfügbarkeit mit eventueller Konsistenz lösen.
Eine der netten Eigenschaften des NGINX-Cache ist, dass er weiterhin zwischengespeicherte Daten bereitstellen kann, selbst wenn der Dienst vollständig nicht verfügbar ist – im obigen Snippet, wenn der Dienst mit einer der vier häufigsten500
-Serienfehlercodes.
Die Zwischenspeicherung ist nicht die einzige Möglichkeit, um auf Clients zu reagieren, auch wenn ein Server ausgefallen ist. Wie bereits im Abschnitt „Das Circuit-Breaker-Muster bietet Flexibilität“ erwähnt, benötigte einer unserer Kunden eine zuverlässige Lösung für den Fall, dass sein personalisierter Anzeigenserver ausfiel. Zwischengespeicherte Antworten waren dafür keine gute Lösung. Stattdessen wollten sie einen generischen Anzeigenserver, der allgemeine Anzeigen bereitstellt, bis der personalisierte Server wieder online ist. Dies lässt sich problemlos durch die Verwendung des Backup-
Parameters für die Serverdirektive
erreichen. Der folgende Codeausschnitt gibt an, dass, wenn alle für die Domäne „Personal Ad Server“ definierten Server nicht verfügbar sind, stattdessen die für die Domäne „Generic Ad Server“ definierten Server verwendet werden.
Upstream Personal-Ad-Server { Server Personal-Ad-Server;
Server Generic-Ad-Server Backup;
Zone Backend 64k;
Least_Time Last_Byte;
Keepalive 300;
}
Und schließlich ist es möglich, die Antwortcodes eines Dienstes von NGINX auswerten und einzeln verarbeiten zu lassen. Wenn im folgenden Snippet ein Dienst ein503
Fehler, sendet NGINX Plus die Anfrage an einen alternativen Dienst weiter. Wenn der Resizer beispielsweise über diese Funktion verfügt und die lokale Instanz überlastet ist oder nicht mehr funktioniert, werden Anforderungen an eine andere Instanz des Resizers gesendet.
Standort / { Fehlerseite 503 = @fallback;
}
Standort @fallback {
Proxy-Pass http://alternative-backend;
}
Das Circuit-Breaker-Muster ist ein leistungsstarkes Tool, um Ausfallsicherheit und Kontrolle in Ihrer Microservices-Anwendung zu gewährleisten. NGINX Plus bietet viele Funktionen und Optionen für die Implementierung des Leistungsschalters in Ihre Umgebung. Der Schlüssel zur Implementierung des Circuit-Breaker-Musters besteht darin, das Fehlerprofil des zu schützenden Dienstes zu verstehen und dann die Optionen auszuwählen, die Ausfälle möglichst verhindern und die Auswirkungen von Ausfällen am besten abmildern, wenn sie auftreten.
Um NGINX Plus auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen.
„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."