Modello Bulkhead

  • 03/19/2020
  • 4 minuti per leggere
    • d
    • D
    • a
    • v
    • d
    • +6

Il modello Bulkhead è un tipo di design di applicazione che è tollerante al fallimento. In un’architettura Bulkhead, gli elementi di un’applicazione sono isolati in pool in modo che se uno di essi fallisce, gli altri continuano a funzionare. Prende il nome dalle partizioni sezionate (paratie) dello scafo di una nave. Se lo scafo di una nave è compromesso, solo la sezione danneggiata si riempie d’acqua, il che impedisce alla nave di affondare.

Contesto e problema

Un’applicazione basata sul cloud può includere più servizi, e ogni servizio ha uno o più consumatori. Un carico eccessivo o un guasto in un servizio avrà un impatto su tutti i consumatori del servizio.

Inoltre, un consumatore può inviare richieste a più servizi contemporaneamente, utilizzando risorse per ogni richiesta. Quando il consumatore invia una richiesta a un servizio che è mal configurato o che non risponde, le risorse utilizzate dalla richiesta del cliente potrebbero non essere liberate in modo tempestivo. Man mano che le richieste al servizio continuano, queste risorse possono esaurirsi. Per esempio, il pool di connessioni del client potrebbe essere esaurito. A quel punto, le richieste del consumatore ad altri servizi ne risentono. Alla fine il consumatore non può più inviare richieste ad altri servizi, non solo al servizio originale che non risponde.

Lo stesso problema di esaurimento delle risorse colpisce i servizi con più consumatori. Un gran numero di richieste provenienti da un cliente può esaurire le risorse disponibili nel servizio. Altri consumatori non sono più in grado di consumare il servizio, causando un effetto di fallimento a cascata.

Soluzione

Partizionare le istanze del servizio in diversi gruppi, basati sul carico dei consumatori e sui requisiti di disponibilità. Questo design aiuta a isolare i guasti e permette di sostenere la funzionalità del servizio per alcuni consumatori, anche durante un guasto.

Un consumatore può anche partizionare le risorse, per garantire che le risorse utilizzate per chiamare un servizio non influenzino le risorse utilizzate per chiamare un altro servizio. Per esempio, ad un consumatore che chiama più servizi può essere assegnato un pool di connessioni per ogni servizio. Se un servizio inizia a fallire, ciò influisce solo sul pool di connessioni assegnato a quel servizio, permettendo al consumatore di continuare a usare gli altri servizi.

I benefici di questo schema includono:

  • Isolleva consumatori e servizi da fallimenti a cascata. Un problema che colpisce un consumatore o un servizio può essere isolato all’interno della propria paratia, evitando che l’intera soluzione fallisca.
  • Consente di preservare alcune funzionalità in caso di guasto di un servizio. Altri servizi e caratteristiche dell’applicazione continueranno a funzionare.
  • Consente di distribuire servizi che offrono una diversa qualità di servizio per le applicazioni consumatrici. Un pool di consumatori ad alta priorità può essere configurato per utilizzare servizi ad alta priorità.

Il seguente diagramma mostra paratie strutturate intorno a pool di connessioni che chiamano singoli servizi. Se il servizio A fallisce o causa qualche altro problema, il pool di connessioni è isolato, quindi solo i carichi di lavoro che utilizzano il pool di thread assegnato al servizio A sono interessati. I carichi di lavoro che usano i servizi B e C non sono interessati e possono continuare a lavorare senza interruzioni.

Il prossimo diagramma mostra più client che chiamano un singolo servizio. Ad ogni client è assegnata un’istanza di servizio separata. Il client 1 ha fatto troppe richieste e ha sovraccaricato la sua istanza. Poiché ogni istanza di servizio è isolata dalle altre, gli altri clienti possono continuare a fare chiamate.

I problemi e considerazioni

  • Definire le partizioni intorno al business e ai requisiti tecnici dell’applicazione.
  • Quando si partizionano servizi o consumatori in paratie, considerare il livello di isolamento offerto dalla tecnologia e l’overhead in termini di costi, prestazioni e gestibilità.
  • Considerare la combinazione di paratie con modelli di retry, circuit breaker e throttling per fornire una gestione dei guasti più sofisticata.
  • Quando si partizionano consumatori in paratie, considerare l’uso di processi, pool di thread e semafori. Progetti come resilience4j e Polly offrono un framework per creare paratie di consumatori.
  • Quando si partizionano i servizi in paratie, considerare di distribuirli in macchine virtuali separate, contenitori o processi. I contenitori offrono un buon equilibrio di isolamento delle risorse con un overhead abbastanza basso.
  • I servizi che comunicano usando messaggi asincroni possono essere isolati attraverso diversi set di code. Ogni coda può avere un insieme dedicato di istanze che elaborano i messaggi sulla coda, o un singolo gruppo di istanze che usa un algoritmo per de-escludere e distribuire l’elaborazione.
  • Determinare il livello di granularità per le paratie. Per esempio, se volete distribuire i tenant tra le partizioni, potete mettere ogni tenant in una partizione separata, o mettere diversi tenant in una partizione.
  • Monitorare le prestazioni e lo SLA di ogni partizione.

Quando usare questo schema

Utilizzare questo schema per:

  • Isoltare le risorse usate per consumare un insieme di servizi di backend, specialmente se l’applicazione può fornire un certo livello di funzionalità anche quando uno dei servizi non risponde.
  • Isolate i consumatori critici dai consumatori standard.
  • Proteggete l’applicazione dai fallimenti a cascata.

Questo pattern può non essere adatto quando:

  • L’uso meno efficiente delle risorse può non essere accettabile nel progetto.
  • La complessità aggiunta non è necessaria

Esempio

Il seguente file di configurazione di Kubernetes crea un contenitore isolato per eseguire un singolo servizio, con le proprie risorse e limiti di CPU e memoria.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.