Resilience

resilience1CircuitBreaker

Path
pkg18resiliencepatterns/resilience1CircuitBreaker.java
Package
pkg18resiliencepatterns
Study order
1
Run
Single-file source launch
Command
java pkg18resiliencepatterns/resilience1CircuitBreaker.java
Lesson
Back to the chapter

There is no in-browser runner. This is the file from the curriculum, unchanged.

pkg18resiliencepatterns/resilience1CircuitBreaker.java
1package pkg18resiliencepatterns;2 3import java.time.Duration;4import java.util.concurrent.atomic.AtomicInteger;5import java.util.function.Supplier;6 7/*8 * resilience1CircuitBreaker.java9 * --------------------------------10 * Circuit breaker: stop calling a failing dependency until it recovers.11 *12 * DEFINITION:13 *   States: CLOSED (normal) -> OPEN (fail fast) -> HALF_OPEN (probe) -> CLOSED/OPEN.14 *   Prevents cascading failures and wasted resources on a dead service.15 *16 * KEY POINTS:17 *   - Trip to OPEN after failure threshold in a window.18 *   - After cooldown, allow one probe (HALF_OPEN); success closes, failure reopens.19 *   - Production: Resilience4j, Spring Cloud Circuit Breaker.20 */21public class resilience1CircuitBreaker {22 23    enum State { CLOSED, OPEN, HALF_OPEN }24 25    static class CircuitBreaker {26        private final int failureThreshold;27        private final long cooldownMs;28        private State state = State.CLOSED;29        private int failures = 0;30        private long openedAt = 0;31 32        CircuitBreaker(int failureThreshold, long cooldownMs) {33            this.failureThreshold = failureThreshold;34            this.cooldownMs = cooldownMs;35        }36 37        <T> T execute(Supplier<T> call) {38            if (state == State.OPEN) {39                if (System.currentTimeMillis() - openedAt >= cooldownMs) {40                    state = State.HALF_OPEN;41                    System.out.println("  breaker -> HALF_OPEN (probe)");42                } else {43                    throw new RuntimeException("Circuit OPEN — fail fast");44                }45            }46            try {47                T result = call.get();48                onSuccess();49                return result;50            } catch (RuntimeException e) {51                onFailure();52                throw e;53            }54        }55 56        private void onSuccess() {57            failures = 0;58            if (state != State.CLOSED) {59                state = State.CLOSED;60                System.out.println("  breaker -> CLOSED");61            }62        }63 64        private void onFailure() {65            failures++;66            if (state == State.HALF_OPEN || failures >= failureThreshold) {67                state = State.OPEN;68                openedAt = System.currentTimeMillis();69                System.out.println("  breaker -> OPEN (failures=" + failures + ")");70                failures = 0;71            }72        }73    }74 75    public static void main(String[] args) throws InterruptedException {76        AtomicInteger attempts = new AtomicInteger();77        CircuitBreaker breaker = new CircuitBreaker(2, 500);78        Supplier<String> flaky = () -> {79            attempts.incrementAndGet();80            throw new RuntimeException("service down");81        };82 83        for (int i = 1; i <= 5; i++) {84            try {85                breaker.execute(flaky);86            } catch (RuntimeException e) {87                System.out.println("call " + i + ": " + e.getMessage());88            }89            Thread.sleep(150);90        }91        System.out.println("Total attempts to flaky service: " + attempts.get() + " (breaker limited calls)");92    }93}