Skip to content

Circuit Breaker with Resilience4j — Production-Ready Java Implementation Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Circuit Breaker with Resilience4j. We cover key concepts, practical examples, and best practices to help you master this topic.

Resilience4j provides a production-ready circuit breaker library for Java with sliding window statistics, configurable failure rate thresholds, wait duration, event publishing, and seamless Spring Boot integration for microservices resilience.

flowchart TD
    R[Request] --> CB[Resilience4j
CircuitBreaker] CB -->|Closed| S[Call Service] S -->|Success| Ok[Return] S -->|Fail| Count[Count Failure] Count -->|Rate > Threshold| Open[Open Circuit] Open -->|Wait Duration| Half[Half-Open] Half -->|Probe Success| Close[Close Circuit] Half -->|Probe Fail| Reopen[Re-open] CB -->|Events| Event[Event Publisher]

What You'll Learn

  • Resilience4j circuit breaker configuration
  • Sliding window statistics
  • Event handling and notification
  • Spring Boot integration
  • Metrics and monitoring

Why It Matters

Resilience4j is the modern standard for Java circuit breakers, replacing Hystrix. It provides lightweight, modular resilience primitives with functional programming support and deep integration with Spring Boot, Micrometer, and Reactor.

Real-World Use

DodaTech's Java microservices use Resilience4j with Spring Boot. Each service gateway has circuit breakers configured with 100-request sliding Windows, 50% failure thresholds, and 30-second wait durations. The circuit breaker events feed a Grafana dashboard showing per-service health.

Basic Configuration

import io.github.resilience4j.circuitbreaker.*;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import java.time.Duration;
import java.util.function.Supplier;

public class CircuitBreakerExample {
    public static void main(String[] args) {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            .slidingWindowSize(10)
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofSeconds(30))
            .permittedNumberOfCallsInHalfOpenState(3)
            .recordExceptions(IOException.class, TimeoutException.class)
            .build();

        CircuitBreaker circuitBreaker = CircuitBreaker
            .of("payment-service", config);

        circuitBreaker.getEventPublisher()
            .onStateTransition(event ->
                System.out.println("State: " + event.getStateTransition()))
            .onFailureRateExceeded(event ->
                System.out.println("Failure rate exceeded: "
                    + event.getFailureRate()));

        Supplier<String> decorated = CircuitBreaker
            .decorateSupplier(circuitBreaker,
                () -> callPaymentService());

        for (int i = 0; i < 15; i++) {
            try {
                String result = Try.ofSupplier(decorated)
                    .recover(throwable -> "Fallback response")
                    .get();
                System.out.println("Result: " + result);
            } catch (Exception e) {
                System.out.println("Error: " + e.getMessage());
            }
        }
    }

    static String callPaymentService() {
        if (Math.random() < 0.6) throw new RuntimeException("Service error");
        return "Payment processed";
    }
}

Expected output:

State: CLOSED_TO_OPEN
Result: Payment processed
Result: Fallback response
Failure rate exceeded: 60.0
State: OPEN_TO_HALF_OPEN
Result: Payment processed
State: HALF_OPEN_TO_CLOSED

Spring Boot Integration

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;

@Service
public class ProductService {
    @CircuitBreaker(name = "catalog-service",
        fallbackMethod = "getProductFallback")
    public Product getProduct(String productId) {
        return catalogClient.fetchProduct(productId);
    }

    public Product getProductFallback(String productId,
            Throwable throwable) {
        System.out.println("Fallback for product " + productId
            + ": " + throwable.getMessage());
        return new Product(productId, "Unavailable", 0.0);
    }
}

Application properties:

resilience4j.circuitbreaker.instances.catalog-service.sliding-window-size=100
resilience4j.circuitbreaker.instances.catalog-service.failure-rate-threshold=50
resilience4j.circuitbreaker.instances.catalog-service.wait-duration-in-open-state=30s
resilience4j.circuitbreaker.instances.catalog-service.permitted-number-of-calls-in-half-open-state=5
resilience4j.circuitbreaker.instances.catalog-service.slow-call-rate-threshold=50
resilience4j.circuitbreaker.instances.catalog-service.slow-call-duration-threshold=5s

Expected Spring Boot log output:

CircuitBreaker 'catalog-service' recorded a failure
CircuitBreaker 'catalog-service' state changed from CLOSED to OPEN
CircuitBreaker 'catalog-service' recorded a call in OPEN state

Metrics Integration

import io.github.resilience4j.micrometer.*;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;

public class MetricsExample {
    public static void main(String[] args) {
        PrometheusMeterRegistry registry =
            new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

        CircuitBreaker circuitBreaker = CircuitBreaker
            .ofDefaults("order-service");

        TaggedCircuitBreakerMetrics
            .ofCircuitBreakerRegistry(
                circuitBreaker.getCircuitBreakerRegistry())
            .bindTo(registry);

        circuitBreaker.getEventPublisher()
            .onCallNotPermitted(event ->
                System.out.println("Call blocked: "
                    + event.getCircuitBreakerName()));

        String metrics = registry.scrape();
        System.out.println("Metrics:");
        System.out.println(metrics);
    }
}

Expected output:

Metrics:
resilience4j_circuitbreaker_state{name="order-service",state="closed"} 1
resilience4j_circuitbreaker_calls{name="order-service",kind="successful"} 42
resilience4j_circuitbreaker_calls{name="order-service",kind="failed"} 5
resilience4j_circuitbreaker_calls{name="order-service",kind="not_permitted"} 3

Common Mistakes

  • Sliding window too small -- a window of 5 samples gives high variance. Set sliding_window_size to 20-100 for stable failure rate estimates. Use time-based sliding windows for variable traffic patterns.
  • Failure rate threshold too low -- 10% failure rate trips on 1 failure in a 10-sample window. Start at 50% and adjust based on service reliability. Payment services tolerate lower thresholds than recommendation services.
  • No slow call threshold -- Resilience4j treats slow calls as failures by default. Set slow_call_duration_threshold to the service's p99 latency. Calls exceeding this count toward the failure rate.
  • Ignoring half-open configuration -- default half-open permits 3 calls. For high-traffic services, increase to 10-20 for faster recovery. Monitor half-open probe success rate separately from normal traffic.
  • No fallback method -- without a fallback method, circuit breaker calls throw CircuitBreakerOpenException. Define fallbacks for all @CircuitBreaker-annotated methods with appropriate degraded responses.

Practice Questions

  1. How does Resilience4j's sliding window differ from simple counters?
  2. What is the purpose of the half-open state in Resilience4j?
  3. How do you configure Resilience4j in Spring Boot?
  4. What metrics does Resilience4j expose for monitoring?
  5. How do slow calls affect circuit breaker behavior?

Challenge

Build a complete Resilience4j circuit breaker setup: (1) 3 circuit breakers for payment, inventory, and shipping services, (2) 100-count sliding window with 50% failure threshold, (3) slow call threshold of 5 seconds with 30% slow call rate threshold, (4) Spring Boot @CircuitBreaker annotations with fallback methods returning degraded responses, (5) Micrometer metrics exported to Prometheus, (6) event listeners that log state transitions and feed a monitoring dashboard, (7) custom exception recording that ignores specific exceptions (like validation errors).

FAQ

How does Resilience4j differ from Hystrix?

Resilience4j is modular (separate circuit breaker, retry, bulkhead, rate limiter modules), uses functional programming, supports reactive (Reactor/RxJava), and integrates with Micrometer. Hystrix is deprecated with no active development.

What sliding window type should I use?

Use COUNT_BASED for predictable traffic or TIME_BASED for variable traffic. COUNT_BASED: last N calls. TIME_BASED: calls in the last N seconds. TIME_BASED adapts better to traffic patterns but uses more memory.

How does Resilience4j handle concurrent requests?

Resilience4j's circuit breaker is thread-safe and uses atomic operations for state management. It does not block threads in open state -- it throws CircuitBreakerOpenException immediately for fast-fail behavior.

Can I use Resilience4j with reactive streams?

Yes. Resilience4j provides Reactor and RxJava operators. Use circuitBreaker.transformFlux(flux) or decorate reactive publishers with CircuitBreakerOperator. Reactive support handles backpressure correctly.

How do I configure Resilience4j without Spring Boot?

Use the builder pattern: CircuitBreakerConfig.custom()...build(). Register the circuit breaker in a CircuitBreakerRegistry. Decorate functions with CircuitBreaker.decorateSupplier() or CircuitBreaker.decorateCheckedSupplier().

Mini Project

Build a Spring Boot microservice with full Resilience4j integration: (1) 4 circuit breakers for different external service dependencies, (2) combined with retry (max 3, exponential backoff) and bulkhead (5 concurrent calls) patterns, (3) custom fallback responses per circuit breaker with stale cached data, (4) Micrometer + Prometheus endpoint exposing circuit breaker metrics, (5) Actuator health endpoint showing circuit breaker states, (6) scheduled task that tests circuit breaker recovery and logs state transitions, (7) integration tests that verify circuit breaker opens and recovers correctly.

What's Next

Continue with Hystrix Migration to learn migrating from Hystrix to Resilience4j. Then explore Python Implementation for Python-based circuit breaker patterns.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro