Bulkhead-Muster

  • 03/19/2020
  • 4 Minuten zu lesen
    • d
    • D
    • a
    • v
    • d
    • +6

Das Bulkhead-Muster ist eine Art von Anwendungsdesign, das gegenüber Fehlern tolerant ist. In einer Bulkhead-Architektur werden die Elemente einer Anwendung in Pools isoliert, so dass bei einem Ausfall eines Elements die anderen weiterhin funktionieren. Der Name leitet sich von den unterteilten Trennwänden (Schotten) eines Schiffsrumpfes ab. Wenn der Rumpf eines Schiffes beschädigt wird, füllt sich nur der beschädigte Teil mit Wasser und verhindert so den Untergang des Schiffes.

Kontext und Problem

Eine Cloud-basierte Anwendung kann mehrere Dienste umfassen, wobei jeder Dienst einen oder mehrere Verbraucher hat. Eine übermäßige Belastung oder ein Ausfall eines Dienstes wirkt sich auf alle Verbraucher des Dienstes aus.

Darüber hinaus kann ein Verbraucher Anfragen an mehrere Dienste gleichzeitig senden und dabei Ressourcen für jede Anfrage verwenden. Wenn der Kunde eine Anfrage an einen Dienst sendet, der falsch konfiguriert ist oder nicht antwortet, werden die von der Anfrage des Kunden verwendeten Ressourcen möglicherweise nicht rechtzeitig freigegeben. Wenn weiterhin Anfragen an den Dienst gestellt werden, können diese Ressourcen erschöpft sein. Zum Beispiel kann der Verbindungspool des Clients erschöpft sein. Dies hat Auswirkungen auf die Anfragen des Kunden an andere Dienste. Schließlich kann der Kunde keine Anfragen mehr an andere Dienste senden, nicht nur an den ursprünglich nicht reagierenden Dienst.

Das gleiche Problem der Ressourcenerschöpfung betrifft Dienste mit mehreren Kunden. Eine große Anzahl von Anfragen, die von einem Kunden ausgehen, kann die verfügbaren Ressourcen des Dienstes erschöpfen. Andere Verbraucher sind nicht mehr in der Lage, den Dienst zu nutzen, was zu einem kaskadenartigen Ausfall führt.

Lösung

Teilen Sie die Dienstinstanzen in verschiedene Gruppen ein, die auf der Verbraucherlast und den Verfügbarkeitsanforderungen basieren. Dieses Design hilft bei der Isolierung von Ausfällen und ermöglicht es Ihnen, die Servicefunktionalität für einige Verbraucher auch während eines Ausfalls aufrechtzuerhalten.

Ein Verbraucher kann auch Ressourcen partitionieren, um sicherzustellen, dass Ressourcen, die für den Aufruf eines Dienstes verwendet werden, die Ressourcen für den Aufruf eines anderen Dienstes nicht beeinträchtigen. Einem Verbraucher, der mehrere Dienste aufruft, kann zum Beispiel für jeden Dienst ein Verbindungspool zugewiesen werden. Wenn ein Dienst ausfällt, wirkt sich dies nur auf den für diesen Dienst zugewiesenen Verbindungspool aus, so dass der Verbraucher weiterhin die anderen Dienste nutzen kann.

Die Vorteile dieses Musters sind unter anderem:

  • Isoliert Verbraucher und Dienste vor kaskadierenden Ausfällen. Ein Problem, das einen Verbraucher oder Dienst betrifft, kann innerhalb seines eigenen Bulkheads isoliert werden, wodurch verhindert wird, dass die gesamte Lösung ausfällt.
  • Ermöglicht es Ihnen, einige Funktionen im Falle eines Dienstausfalls beizubehalten. Andere Dienste und Funktionen der Anwendung funktionieren weiterhin.
  • Ermöglicht die Bereitstellung von Diensten, die eine andere Dienstqualität für konsumierende Anwendungen bieten. Ein Verbraucherpool mit hoher Priorität kann so konfiguriert werden, dass er Dienste mit hoher Priorität verwendet.

Das folgende Diagramm zeigt Bulkheads, die um Verbindungspools herum strukturiert sind, die einzelne Dienste aufrufen. Wenn Dienst A ausfällt oder ein anderes Problem verursacht, wird der Verbindungspool isoliert, sodass nur Workloads betroffen sind, die den dem Dienst A zugewiesenen Thread-Pool verwenden. Workloads, die Dienst B und C verwenden, sind nicht betroffen und können ohne Unterbrechung weiterarbeiten.

Das nächste Diagramm zeigt mehrere Clients, die einen einzelnen Dienst aufrufen. Jedem Client ist eine eigene Dienstinstanz zugeordnet. Client 1 hat zu viele Anfragen gestellt und seine Instanz überlastet. Da jede Dienstinstanz von den anderen isoliert ist, können die anderen Clients weiterhin Anrufe tätigen.

Probleme und Überlegungen

  • Definieren Sie Partitionen anhand der geschäftlichen und technischen Anforderungen der Anwendung.
  • Bei der Partitionierung von Diensten oder Verbrauchern in Bulkheads ist der Grad der von der Technologie gebotenen Isolierung sowie der Overhead in Bezug auf Kosten, Leistung und Verwaltbarkeit zu berücksichtigen.
  • Kombinieren Sie Bulkheads mit Retry-, Circuit-Breaker- und Throttling-Mustern, um eine ausgefeiltere Fehlerbehandlung zu ermöglichen.
  • Bei der Partitionierung von Verbrauchern in Bulkheads ist die Verwendung von Prozessen, Thread-Pools und Semaphoren zu erwägen. Projekte wie resilience4j und Polly bieten einen Rahmen für die Erstellung von Verbraucher-Bulkheads.
  • Wenn Sie Dienste in Bulkheads partitionieren, sollten Sie in Erwägung ziehen, sie in separaten virtuellen Maschinen, Containern oder Prozessen bereitzustellen. Container bieten ein gutes Gleichgewicht zwischen Ressourcenisolierung und relativ geringem Overhead.
  • Dienste, die mit asynchronen Nachrichten kommunizieren, können durch verschiedene Sätze von Warteschlangen isoliert werden. Jede Warteschlange kann über einen dedizierten Satz von Instanzen verfügen, die Nachrichten in der Warteschlange verarbeiten, oder über eine einzelne Gruppe von Instanzen, die einen Algorithmus zum Entkoppeln und Verteilen der Verarbeitung verwenden.
  • Bestimmen Sie die Granularitätsebene für die Bulkheads. Wenn Sie beispielsweise Tenants auf Partitionen verteilen möchten, können Sie jeden Tenant in einer separaten Partition unterbringen oder mehrere Tenants in einer Partition zusammenfassen.
  • Überwachen Sie die Leistung und das SLA jeder Partition.

Wann sollte dieses Muster verwendet werden

Verwenden Sie dieses Muster, um:

  • Ressourcen zu isolieren, die verwendet werden, um eine Reihe von Backend-Diensten zu nutzen, insbesondere wenn die Anwendung ein gewisses Maß an Funktionalität bereitstellen kann, selbst wenn einer der Dienste nicht reagiert.
  • Kritische Verbraucher von Standardverbrauchern isolieren.
  • Die Anwendung vor kaskadierenden Fehlern schützen.

Dieses Muster ist möglicherweise nicht geeignet, wenn:

  • Eine weniger effiziente Nutzung von Ressourcen im Projekt nicht akzeptabel ist.
  • Die zusätzliche Komplexität ist nicht notwendig

Beispiel

Die folgende Kubernetes-Konfigurationsdatei erstellt einen isolierten Container, um einen einzelnen Dienst auszuführen, mit eigenen CPU- und Speicherressourcen und -grenzen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.