Das Bulkhead-Muster ist ein fehlertoleranter Anwendungsentwurfstyp. In einer Bulkhead-Architektur, auch als zellenbasierte Architektur bezeichnet, werden Elemente einer Anwendung in Pools isoliert, sodass bei Ausfall eines Elements die anderen Elemente weiterhin funktionieren. Sie ist nach den Schotts (Bulkheads) benannt, die die Sektionen eines Schiffsrumpfs trennen. Wenn eine Schottwand eines Schiffs beschädigt ist, füllt sich nur der beschädigte Abschnitt mit Wasser, sodass das Schiff nicht sinkt.
Kontext und Problem
Eine cloudbasierte Anwendung kann mehrere Dienste umfassen, von denen jeder einen oder mehrere Consumer aufweist. Eine übermäßige Last oder ein Fehler in einem Dienst wirkt sich auf alle Consumer des Diensts aus.
Außerdem kann ein Consumer Anforderungen an mehrere Dienste gleichzeitig senden und für jede Anforderung Ressourcen nutzen. Wenn der Consumer eine Anforderung an einen Dienst sendet, der falsch konfiguriert ist oder nicht mehr reagiert, werden die für die Anforderung des Clients genutzten Ressourcen möglicherweise nicht rechtzeitig freigegeben. Da die an den Dienst gesendeten Anforderungen bestehen bleiben, werden diese Ressourcen möglicherweise erschöpft. Beispielsweise kann der Verbindungspool des Clients erschöpft sein. Dies beeinträchtigt Anforderungen des Consumers an andere Dienste. Schließlich kann der Consumer nicht nur an den ursprünglich nicht mehr reagierenden Dienst, sondern auch an andere Dienste keine Anforderungen mehr senden.
Das gleiche Problem der Ressourcenauslastung wirkt sich auf Dienste mit mehreren Consumern aus. Eine große Anzahl von Anforderungen eines Clients erschöpft möglicherweise die im Dienst verfügbaren Ressourcen. Andere Consumer können den Dienst nicht mehr nutzen, was einen kaskadierenden Fehler verursacht.
Lösung
Unterteilen Sie Dienstinstanzen in unterschiedliche Gruppen, abhängig von den Anforderungen an Consumerlast und Verfügbarkeit. Dieser Entwurf unterstützt das Isolieren von Fehlern und ermöglicht es Ihnen, auch während eines Fehlers die Dienstfunktionalität für einige Consumer aufrechtzuerhalten.
Ein Consumer kann ebenfalls Ressourcen partitionieren, um sicherzustellen, dass Ressourcen, die zum Aufrufen eines Diensts genutzt werden, die zum Aufrufen anderer Dienste genutzten Ressourcen nicht beeinträchtigen. Beispielsweise kann einem Consumer, der mehrere Dienste aufruft, für jeden Dienst ein Verbindungspool zugewiesen werden. Wenn ein Dienst auszufallen beginnt, wirkt sich dies nur auf den Verbindungspool aus, der für diesen Dienst zugewiesen ist, sodass der Consumer die anderen Dienste weiter verwenden kann.
Vorteile dieses Musters:
- Consumer und Dienste werden von kaskadierenden Fehlern isoliert. Ein Problem, das sich auf einen Consumer oder Dienst auswirkt, kann innerhalb des eigenen Bulkheads isoliert werden, um zu verhindern, dass die gesamte Lösung ausfällt.
- Sie können bei einem Dienstausfall einen Teil der Funktionalität bewahren. Andere Dienste und Features der Anwendung werden weiterhin ausgeführt.
- Sie können Dienste bereitstellen, die für Consumeranwendungen eine andere Dienstqualität bieten. Es kann ein Consumerpool hoher Priorität für die Verwendung von Diensten hoher Priorität konfiguriert werden.
Im folgenden Diagramm sind Bulkheads dargestellt, die Verbindungspools umgeben, 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 Dienst A zugewiesenen Threadpool verwenden. Workloads, die Dienst B und C verwenden, sind nicht betroffen und können ohne Unterbrechung fortgesetzt werden.
Im nächsten Diagramm werden mehrere Clients gezeigt, die einen einzelnen Dienst aufgerufen. Jedem Client ist eine eigene Dienstinstanz zugewiesen. Client 1 hat zu viele Anforderungen gesendet und die zugewiesene Dienstinstanz überlastet. Da jede Dienstinstanz von den anderen isoliert ist, können alle anderen Clients weiterhin Aufrufe senden.
Probleme und Überlegungen
- Definieren Sie Partitionen entsprechend den geschäftlichen und technischen Anforderungen der Anwendung.
- Wenn Sie mit taktischem DDD Microservices entwerfen, müssen die Partitionsbegrenzungen an den Kontextgrenzen ausgerichtet sein.
- Wenn Sie Dienste oder Consumer in Bulkheads unterteilen, berücksichtigen Sie den Grad der von der Technologie gebotenen Isolation sowie den Mehraufwand an Kosten, Leistung und Verwaltbarkeit.
- Möglicherweise empfiehlt es sich, Bulkheads mit Wiederholungs-, Trennschalter- und Drosselungsmustern zu kombinieren, um eine differenziertere Fehlerbehandlung zu ermöglichen.
- Wenn Sie Consumer in Bulkheads unterteilen, sollten Sie möglicherweise Prozesse, Threadpools und Semaphore verwenden. Projekte wie resilience4j und Polly verfügen über ein Framework zum Erstellen von Consumerbulkheads.
- Wenn Sie Dienste in Bulkheads unterteilen, empfiehlt es sich, sie in jeweils eigenen virtuellen Computern, Containern oder Prozessen bereitzustellen. Container bieten eine gut ausgewogene Ressourcenisolation mit relativ geringem Mehraufwand.
- Dienste, die mithilfe asynchroner Nachrichten kommunizieren, können mithilfe unterschiedlicher Gruppen von Warteschlangen isoliert werden. Für jede Warteschlange kann eine dedizierte Gruppe von Instanzen vorhanden sein, die Nachrichten in der Warteschlange verarbeiten, oder eine einzelne Gruppe von Instanzen, die mit einem Algorithmus Nachrichten aus der Warteschlange entfernen und die Verarbeitung disponieren.
- Bestimmen Sie den Grad der Granularität für die Bulkheads. Wenn Sie beispielsweise Mandanten auf Partitionen verteilen möchten, können Sie jeden Mandanten in einer eigenen Partition anordnen oder mehrere Mandanten in einer Partition anordnen.
- Überwachen Sie die Leistung und SLA jeder Partition.
Verwendung dieses Musters
Verwendung Sie dieses Muster für folgende Zwecke:
- Isolieren von Ressourcen, die für die Nutzung eines Satzes von Back-End-Diensten verwendet werden, insbesondere wenn die Anwendung den gleich Grad an Funktionalität auch dann bereitstellen kann, wenn einer der Dienste nicht mehr reagiert.
- Isolieren kritischer Consumer von Standardconsumern.
- Schützen der Anwendung vor kaskadierenden Fehlern.
Dieses Muster ist in folgenden Fällen möglicherweise nicht geeignet:
- Eine weniger effiziente Nutzung von Ressourcen ist im Projekt nicht tolerierbar.
- Die zusätzliche Komplexität ist nicht erforderlich.
Workloadentwurf
Ein Architekt sollte evaluieren, wie das Bulkhead-Muster im Design seiner Workloads verwendet werden kann, um die Ziele und Prinzipien zu erreichen, die in den Säulen des Azure Well-Architected Framework behandelt werden. Zum Beispiel:
Säule | So unterstützt dieses Muster die Säulenziele |
---|---|
Zuverlässigkeitsdesignentscheidungen tragen dazu bei, dass Ihre Workload ausfallsicher wird und dass sie nach einem Ausfall wieder in einen voll funktionsfähigen Zustand zurückkehrt. | Die Fehlerisolationsstrategie, die durch die absichtliche und vollständige Segmentierung zwischen Komponenten eingeführt wird, versucht, Fehler nur auf den Bulkhead zu beschränken, bei dem das Problem auftritt, und verhindert so Auswirkungen auf andere Bulkheads. - RE:02 Kritische Flows - RE:07 Selbsterhaltung |
Sicherheitsdesignentscheidungen tragen dazu bei, die Vertraulichkeit, Integrität und Verfügbarkeit der Daten und Systeme Ihrer Workload sicherzustellen. | Die Segmentierung zwischen Komponenten trägt dazu bei, Sicherheitsvorfälle auf den gefährdete Bulkhead zu beschränken. - SE:04 Segmentierung |
Die Leistungseffizienz hilft Ihrer Workload, Anforderungen effizient durch Optimierungen in Skalierung, Daten und Code zu erfüllen. | Jeder Bulkhead kann individuell skalierbar sein, um die Anforderungen der im Bulkhead gekapselten Aufgabe effizient zu erfüllen. - PE:02 Kapazitätsplanung - PE:05 Skalierung und Partitionierung |
Berücksichtigen Sie wie bei jeder Designentscheidung alle Kompromisse im Hinblick auf die Ziele der anderen Säulen, die mit diesem Muster eingeführt werden könnten.
Beispiel
Die folgende Kubernetes-Konfigurationsdatei erstellt einen isolierten Container zum Ausführen eines einzelnen Diensts, mit eigenen CPU- und Arbeitsspeicherressourcen und eigenen CPU- und Arbeitsspeicherlimits.
apiVersion: v1
kind: Pod
metadata:
name: drone-management
spec:
containers:
- name: drone-management-container
image: drone-service
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "1"