W-JAX Blog mit Gerrit Brehmer

An mehr Robustheit führt kein Weg vorbei

Gerrit Brehmer

Gerrit Brehmer

Gerrit Brehmer ist Software-Architekt und Senior Developer bei der inovex GmbH. Im W-JAX Blog führt er ein Thema aus, das er in seiner Session „Hystrix in Action“ weiter vertiefen wird: Wie baue ich robuste Software? Dabei beginnt er mit einem Zitat von Michael Nygard: „Normal mode of operation is partial failure.“

Michael Nygard spricht hier aus, was eigentlich niemand über seine Software im Betrieb öffentlich zugeben würde. Man könnte diesbezüglich fast von einem Tabuthema sprechen, denn selten wird schon früh in einem Projekt auf die möglichen Fehler-, Überlast- oder Problem-Konstellationen eingegangen und noch seltener wird offensiv und transparent mit diesem heiklen Thema umgegangen.

Dennoch wachsen die Notwendigkeiten stetig, nicht zuletzt wegen den beiden folgenden Themen:

Verfügbarkeit

Betrachtet man das Thema Verfügbarkeit, erkennt man schnell, dass sich ein Dienst im Internet heutzutage keine Downtime erlauben kann, ohne nicht dabei sowohl Kunden als auch an Boden gegenüber dem Konkurrenten zu verlieren.

Man sollte von seinen und anderen Diensten keine 100%ige Verfügbarkeit erwarten. Sobald mehrere Systeme die Gesamtverfügbarkeit bestimmen, sind bereits 99,0% Uptime (= 3,7 Tage Downtime/Jahr) nur noch schwer umzusetzen. Oft wird vergessen, dass die einzelnen Verfügbarkeitswerte bei einer angenommenen direkten Kopplung entsprechend in der Gesamt-Wahrscheinlichkeitsberechnung miteinander multipliziert werden müssen. In einem Beispiel-Fall von vier benötigten externen Abhängigkeiten, die jeweils wie der eigene Dienst über eine Verfügbarkeit von 99,7% verfügen (= 1,1 Tage Downtime/Jahr), sind 99,0% bereits nicht mehr zu erreichen (99,7^1+4 = 98,5%).

Mehr zum Thema verrät Gerrit Brehmer auf der W-JAX 2015 in seiner Session:
Hystrix in Action – Ein Weg zu robuster Software

Jede Software profitiert von mehr Robustheit, insbesondere wenn die Integration von externen Diensten eine Rolle spielt. Hierbei unterstützt Hystrix mit Implementierungen von bekannten Resilience-Patterns wie u.a. „Fail Fast“ und „Graceful degradation“. Anhand von Beispielen aus realen Projekten wird die Praxistauglichkeit bewiesen.Doch Hystrix hilft uns nicht nur im Notfall, wenn ein integrierter Dienst nicht mehr so zuverlässig antwortet wie erwartet, sondern auch zur genaueren Ursachenanalyse. Die wertvollen von Hystrix gesammelten Daten zusammen mit dem mächtigen ELK-Stack ergeben ein ideales Analysetool. Spätestens nach der Demo sollte das Bedürfnis für den sofortigen Einsatz geweckt sein.

Weitere Programminfos unter: www.w-jax.de

Verteilte Systeme

Ebenfalls unverzichtbar ist der Einsatz von verteilten Systemen geworden. Die Trennung geschieht häufig sowohl auf Basis von Schichten als auch durch Abtrennung abgeschlossener Funktionalitäten. Nicht selten werden komplette Prozessbestandteile wie das Vertrags- und Abrechnungsmanagement an externe Dienstleister und deren Services vergeben.

Bei dem immer populärer werdenden Architektur-Paradigma der Microservices wird dieses Vorgehen zudem noch auf die Spitze getrieben. Solche Systeme bestehen aus einer Vielzahl von Services mit entsprechenden Abhängigkeiten untereinander. Die Kommunikation und lose Bindung zwischen den Diensten sind dort zentrale Bestandteile.

Mit einer wachsenden Anzahl von Abhängigkeiten in einem verteilten System steigt die Anforderung an mehr Robustheit und Fehlertoleranz überproportional.

Die Realität

Die von Michael aufgestellte Hypothese entspricht auch meinen persönlichen Erfahrungen der letzten Jahre, insbesondere bei der Integration von externen Systemen. Auch ist es nicht speziell ein Problem größerer Systeme, da es schlussendlich überall passieren kann und Komplettausfälle in den wenigsten Systemen toleriert werden können.

Die typischen Probleme aufgrund von enger Kopplung und Bindung aller verfügbaren Ressourcen sollten jedem bereits in unterschiedlichen Ausprägungen im Betrieb begegnet sein. Diese Situation tritt z.B. ein, wenn alle zur Verfügung stehenden eingehenden Verbindungen der Clients (z.B. begrenzt durch die Anzahl der Threads im Tomcat-Connector) durch eine einzige Abhängigkeit und deren Fehlverhalten gebunden und damit blockiert werden. Weil ein eng gekoppelter Aufruf eines externen Dienstes länger braucht als normal, stauen sich die Client-Verbindungen an, bis alle Verbindungen des Connection-Pools in Verwendung sind. Dies ist auch der Fall, wenn der Client selbst nicht auf die Antwort wartet. Stattdessen wird die Situation noch verschärft, wenn diese Clients ihre Anfrage wiederholen. Sobald der Connection-Pool ausgelastet ist, werden die weiteren Clients ausgesperrt und einzelne oder alle Requests abgelehnt. Als Resultat gibt es einen gar nicht, oder nur rudimentär verfügbaren Dienst, wobei kein Einfluss drauf genommen werden kann, ob kritische oder unkritische Teile des Dienstes für den Client fehlschlagen.

Akzeptanz

Es stellt sich unweigerlich die Frage, wie diese unbefriedigende Situation verbessert werden kann. Zu allererst sollte man sich bewusst machen, dass die Behandlung solcher Situationen auch zum Aufgabengebiet der eigenen Software gehört. Das Wichtigste ist also, sich hier die diversen Fehler-Situationen vor Augen zu führen und zu entscheiden, in welcher Situation welche Reaktion angemessen ist.

Zu solchen Fehlern gehören nicht nur die offensichtlichen Störungen, von denen die eigene Funktionalität abhängt – wie zum Beispiel dauerhafte Fehlerrückgabe beim Aufruf eines integrierten Dienstes -, sondern auch weniger direkt ersichtliche Probleme wie Laufzeit-, Lastprobleme oder Fehlerkaskaden.

Resilience Patterns

Die Problemstellungen sind weder neu, noch sind es Problemstellungen, die nur einen selbst betreffen. Daher gibt es etablierte, gut beschriebene Muster, die unter dem Begriff “Resilience Patterns” zusammengefasst werden. Für das oben genannte Beispiel das Bulkhead-Pattern. Durch den Einsatz von zusätzlichen kleineren Thread-Pools werden beim Aufruf potenzieller Fehlerquellen pro Abhängigkeit oder Funktion die Zugriffe eingeschränkt. Werden  diese  ausgeschöpft, kann die dahinterliegende Funktion keinem weiteren Nutzer zur Verfügung gestellt werden, das restliche System soll hiervon aber nicht in Mitleidenschafft gezogen werden. Michael hat hierfür den Begriff „Bulkheads“ (=Schott) geprägt. Die Schotten in einem Schiff sind so angeordnet, dass bei einem Leck durch das Schließen eines Schotts nur ein bestimmter Bereich isoliert werden kann und das Sinken des Schiffes verhindert wird.

Unterstützung durch Hystrix

Anstelle das Rad neu zu erfinden, bietet es sich wie so häufig an, nach bestehenden Bibliotheken und Tools Ausschau zu halten. Hystrix von Netflix bietet mit seiner Implementierung teils komplette Lösungen, teils Hilfestellung zur Umsetzung gängiger Resilience-Patterns wie zum Beispiel dem oben beschriebenen Bulkhead. Als Grundlage weiterer Patterns wie “Fail Fast” & “Graceful Degradation” dient bei Hystrix die Implementierung des Circuit-Breaker-Patterns, das ebenfalls von Michael in seinem Buch “Release It!” beschrieben wird. Hierbei wird ähnlich wie bei einem Schutzschalter im Fehlerfall der Stromkreis geöffnet und keine weiteren Anfragen zum fehlerhaften Service weitergeleitet. Der Fehlerfall kann durch Festlegen von Timeout-Grenzen, Mindest-Fehleranteil und -Fehlerzahl auf die eigenen Bedürfnisse angepasst werden. Anders wie beim Schutzschalter in der Elektroverteilung schließt sich der Schalter bei Hystrix aber von selbst, sobald nach einem frei definierbaren Zeitraum erfolgreiche Aufrufe durchgeführt werden können. Damit ist es mit wenig Aufwand möglich, eine Entkopplung vorzunehmen, Fehlersituationen zeitnah zu erkennen und falls notwendig geeignete Alternativen anzusprechen.

Mit Hystrix werden die Aufwände für robustere Systeme stark vereinfacht. Grundsätzliche Ursachen der Probleme werden nicht gelöst, aber dadurch, dass die eigene Anwendung robuster und fehlertoleranter reagiert, werden die Folgen abgeschwächt – im Idealfall so weit, dass der Nutzer trotz eines Teilausfalls keine Einschränkungen bemerkt. In nahezu allen Systemen, in denen externe Systeme integriert sind und es verteilte Aufrufe über Netzwerke hinweg gibt, sollte die Verwendung zumindest betrachtet und abgewogen werden.

Verwandte Themen:

Geschrieben von
Gerrit Brehmer
Gerrit Brehmer
Gerrit Brehmer ist Softwarearchitekt und Senior Developer bei der inovex GmbH. Er beschäftigt sich hierbei insbesondere mit der Architektur robuster Webanwendungen. Dabei wechselt er auch gern mal die Rollen und steuert Projekte mit agilen Methoden ins Ziel.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: