Lab 06: Microservices Patterns
Overview
Step 1: Circuit Breaker
// Circuit Breaker: CLOSED -> OPEN -> HALF_OPEN -> CLOSED/OPEN
class CircuitBreaker {
static STATES = { CLOSED: 'CLOSED', OPEN: 'OPEN', HALF_OPEN: 'HALF_OPEN' };
#fn; #state; #failures; #successes; #nextAttempt;
#threshold; #successThreshold; #timeout; #onStateChange;
constructor(fn, options = {}) {
this.#fn = fn;
this.#state = CircuitBreaker.STATES.CLOSED;
this.#failures = 0;
this.#successes = 0;
this.#threshold = options.threshold ?? 5;
this.#successThreshold = options.successThreshold ?? 2;
this.#timeout = options.timeout ?? 30000;
this.#onStateChange = options.onStateChange ?? (() => {});
this.#nextAttempt = Date.now();
}
get state() { return this.#state; }
get metrics() { return { state: this.#state, failures: this.#failures, successes: this.#successes }; }
#transition(newState) {
if (this.#state !== newState) {
this.#onStateChange(this.#state, newState);
this.#state = newState;
}
}
async call(...args) {
if (this.#state === CircuitBreaker.STATES.OPEN) {
if (Date.now() < this.#nextAttempt) {
throw new Error(`Circuit breaker OPEN. Retry after ${new Date(this.#nextAttempt).toISOString()}`);
}
this.#transition(CircuitBreaker.STATES.HALF_OPEN);
this.#successes = 0;
}
try {
const result = await this.#fn(...args);
this.#onSuccess();
return result;
} catch (err) {
this.#onFailure();
throw err;
}
}
#onSuccess() {
this.#failures = 0;
if (this.#state === CircuitBreaker.STATES.HALF_OPEN) {
this.#successes++;
if (this.#successes >= this.#successThreshold) {
this.#transition(CircuitBreaker.STATES.CLOSED);
}
}
}
#onFailure() {
this.#failures++;
if (this.#failures >= this.#threshold || this.#state === CircuitBreaker.STATES.HALF_OPEN) {
this.#nextAttempt = Date.now() + this.#timeout;
this.#transition(CircuitBreaker.STATES.OPEN);
}
}
}
// Demo
let callCount = 0;
const unstableService = async () => {
callCount++;
if (callCount <= 3) throw new Error('Service down');
return 'Success';
};
const cb = new CircuitBreaker(unstableService, {
threshold: 3,
onStateChange: (from, to) => console.log(`Circuit: ${from} -> ${to}`)
});
(async () => {
for (let i = 0; i < 5; i++) {
try {
const r = await cb.call();
console.log(`Call ${i+1}: ${r} [${cb.state}]`);
} catch (e) {
console.log(`Call ${i+1}: ${e.message.slice(0,30)}... [${cb.state}]`);
}
}
})();Step 2: Bulkhead Pattern
Step 3: Retry Pattern
Step 4: Structured Logging
Step 5: Health Checks
Step 6: Graceful Shutdown
Step 7: Service Discovery (Simple)
Step 8: Capstone — Circuit Breaker State Machine
Summary
Pattern
Problem Solved
Implementation
Last updated
