Overview

Learn how to make your microservice calls resilient using the Circuit Breaker pattern with Resilience4j and Spring Boot — complete demo, step-by-step commands, class-by-class explanations, sample outputs, real-world use cases, and production tips.

Why This Matters

In distributed systems, a failing downstream service can cascade and cause overall system outages. The Circuit Breaker pattern:

This reduces downtime, protects thread pools, keeps user experience reasonable, and prevents retry storms.

Demo Summary (What You Have)

A two-service Maven demo:

End point

GET /api/hello

End point

GET /api/get-message

Run both (hello-service and client-service)

Then test

GET http://localhost:8080/api/get-message

Architecture Diagram

This is a small, focused flow suitable for drawing a diagram

Files & Code: Class-By-Class Explanation

hello-service

HelloServiceApplication.java

HelloController.java

@RestController
public class HelloController {

    private static int counter = 0;

    @GetMapping("/api/hello")
    public String sayHello() {
        counter++;
        // simulate intermittent failure: fail on every 3rd request
        if (counter % 3 == 0) {
            throw new RuntimeException("Simulated failure from Hello-Service!");
        }
        return "Hello from Hello-Service! (count=" + counter + ")";
    }
}

Explanation: This controller intentionally throws a RuntimeException on periodic calls to simulate transient failures you’d see in real systems (DB outage, bad data, timeouts).

client-service

ClientServiceApplication.java

AppConfig.java
@Configuration
public class AppConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Explanation: Provides a single RestTemplate bean. Ensure RestTemplate is a Spring bean so AOP/resilience proxies can work properly.

HelloClientService.java
@Service
public class HelloClientService {

    private final RestTemplate restTemplate;

    @Value("${hello.service.url}")
    private String helloServiceUrl;

    public HelloClientService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @CircuitBreaker(name = "helloService", fallbackMethod = "fallbackHello")
    public String getHelloMessage() {
        System.out.println("Calling hello service: " + helloServiceUrl);
        return restTemplate.getForObject(helloServiceUrl, String.class);
    }

    public String fallbackHello(Throwable t) {
        System.out.println("Fallback triggered: " + t);
        return "Hello Service is currently unavailable. Please try again later.";
    }
}

Explanation (crucial bits)

ClientController.java
@RestController
public class ClientController {

    private final HelloClientService helloClientService;

    public ClientController(HelloClientService helloClientService) {
        this.helloClientService = helloClientService;
    }

    @GetMapping("/api/get-message")
    public String getMessage() {
        return helloClientService.getHelloMessage();
    }
}

Explanation: Simple controller delegating to HelloClientService. This ensures the call goes through the proxy where the circuit breaker is applied.

Configuration Used (client-service application.properties)

Key configuration used in the demo

server.port=8080
spring.application.name=client-service

hello.service.url=http://localhost:8081/api/hello

resilience4j.circuitbreaker.instances.helloService.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.helloService.slidingWindowSize=5
resilience4j.circuitbreaker.instances.helloService.minimumNumberOfCalls=2
resilience4j.circuitbreaker.instances.helloService.failureRateThreshold=50
resilience4j.circuitbreaker.instances.helloService.waitDurationInOpenState=10s

logging.level.io.github.resilience4j.circuitbreaker=DEBUG

Meaning of important properties

Step-By-Step Run & Expected Outputs

Start Services

  1. Start hello-service
Visit: http://localhost:8081/api/hello

Returns → "Hello from Hello-Service!" (or throws simulated failure)

  1. Start client-service
Visit: http://localhost:8080/api/get-message

Test Scenarios & Outputs

Scenario A — Hello-Service Healthy

Make a request

GET http://localhost:8080/api/get-message

Client logs

Calling hello service: http://localhost:8081/api/hello
2025-11-13T11:58:23.366+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-8] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' succeeded:
2025-11-13T11:58:23.366+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-8] i.g.r.c.i.CircuitBreakerStateMachine : Event SUCCESS published: 2025-11-13T11:58:23.366634+05:30[Asia/Calcutta]: CircuitBreaker 'helloService' recorded a successful call. Elapsed time: 15 ms

Response

Hello from Hello-Service! (count=4)

Scenario B — Hello-Service Intermittent Failures

If you call repeatedly and hello-service throws RuntimeException on some requests:

● Successful calls: client returns the hello message.

●  When a downstream call returns HTTP 500/exception:

            o    Resilience4j records the failure.

            o    If failure rate exceeds threshold (e.g., 50% over sliding window), the Circuit becomes **OPEN**.

            o    While OPEN, calls are short-circuited; **fallbackHello**() is immediately executed — no network call. \n                       **Client response while fallback active**

Client Response while active

Response

Hello Service is currently unavailable. Please try again later.

Sample client log sequence

Calling hello service: http://localhost:8081/api/hello
2025-11-13T12:00:55.842+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' recorded an exception as failure:

Scenario C — Recovery

After waitDurationInOpenState (10s):

Real-World Use Cases

  1. Payment Integration - A checkout service calling an external bank API: if the bank’s API becomes slow/fails, the circuit breaker returns a user-friendly message and prevents retry storms.
  2. Third-party rate-limited APIs - APIs with limited calls per second — when limits are reached, the circuit opens to avoid hitting quotas further; fallback returns cached data.
  3. Microservice chains inside an enterprise - Service A calls B, which calls C. If C is unstable, open breakers protect B and A, preventing system-wide slowdown.
  4. Feature toggles and graceful degradation - When a non-critical feature service fails, return a simpler result to keep core functionality available (e.g., return product list without recommendations).

Advantages & Business Value

Sample logs you’ll see (realistic)

Calling hello service: http://localhost:8081/api/hello
2025-11-13T12:00:55.842+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' recorded an exception as failure
2025-11-13T12:00:55.847+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : Event ERROR published: 2025-11-13T12:00:55.847908200+05:30[Asia/Calcutta]: CircuitBreaker 'helloService' recorded an error: 'org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : "{"timestamp":"2025-11-13T06:30:55.842+00:00","status":500,"error":"Internal Server Error","path":"/api/hello"}"'. Elapsed time: 8 ms
Fallback triggered: org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : "{"timestamp":"2025-11-13T06:30:55.842+00:00","status":500,"error":"Internal Server Error","path":"/api/hello"}"