Tag: architecture, microservices, resilience, circuit-breaker, retry, distributed-systems
Livello: intermedio
TL;DR: Pattern per prevenire guasti a catena e garantire continuita' operativa nei sistemi distribuiti.
Circuit Breaker isola i servizi degradati evitando sovraccarichi continui.
Stati del Circuit Breaker: Closed (normale), Open (blocco chiamate), Half-Open (test ripristino).
Retry gestisce errori transitori con tentativi ripetuti e distanziati.
Exponential Backoff con Jitter riduce la contesa introducendo ritardi casuali crescenti.
Bulkhead isola risorse e pool di thread per evitare crash totali.
Timeout interrompe chiamate bloccanti prevenendo l'esaurimento delle risorse locali.
Fallback fornisce una risposta degradata o predefinita in caso di errore.
Rate Limiting e Throttling proteggono i nodi downstream da picchi imprevisti.
Idempotenza e' requisito fondamentale per applicare retry in sicurezza.
Q: Come decidi quando applicare un Retry rispetto a un Circuit Breaker?R: Uso il Retry per fallimenti transitori e brevi come glitch di rete; applico il Circuit Breaker quando i fallimenti sono sistematici, per dare tempo al downstream di recuperare senza saturare risorse.
Q: Perche' si aggiunge il Jitter all'Exponential Backoff?R: Evita il problema del Thundering Herd, impedendo a molti client sincronizzati di colpire il servizio simultaneamente allo stesso intervallo.
Q: Come garantisci la sicurezza delle chiamate ripetute con Retry?R: Mi assicuro che le operazioni siano idempotenti, usando idempotency key univoche per prevenire duplicazioni di effetti collaterali su scritture e pagamenti.
Retry storm causato da tentativi su errori permanenti tipo 400 o 404.
Assenza di timeout reattivi che blocca thread pool e causa memory leak.
Fallback che chiama a sua volta servizi instabili propagando l'errore.
Soglie del Circuit Breaker tarate senza considerare il volume reale di traffico.
Retry eseguiti su endpoint non idempotenti causando mutazioni duplicate.
// Esempio logica Circuit Breaker essenziale
class SimpleCircuitBreaker {
private failures = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private nextAttempt = Date.now();
constructor(private threshold = 5, private timeoutMs = 10000) {}
async execute<T>(action: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) throw new Error('Circuit is OPEN');
this.state = 'HALF_OPEN';
}
try {
const res = await action();
this.reset();
return res;
} catch (err) {
this.recordFailure();
throw err;
}
}
private recordFailure() {
this.failures++;
if (this.failures >= this.threshold || this.state === 'HALF_OPEN') {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeoutMs;
}
}
private reset() {
this.failures = 0;
this.state = 'CLOSED';
}
}
[ ] Taratura soglie di failure rate e finestre temporali nel Circuit Breaker
[ ] Implementazione di token bucket e leaky bucket per il Rate Limiting
[x] Concetto di isolamento tramite Bulkhead su thread pool distinti
[x] Differenza tra errori transitori (503, timeout) ed errori permanenti (4xx)