Circuit Breaker Pattern in Microservices: How It Works and How to Implement It

circuit breaker pattern in Microservices

When a service in a microservices architecture becomes slow or unavailable, the services that depend on it can also start failing. Repeated requests, long waits, and exhausted resources can turn a small problem into a system-wide outage. This is known as a cascading failure.

The circuit breaker pattern in microservices helps prevent this situation by temporarily stopping requests to an unhealthy service. Instead of repeatedly calling a failing dependency, the application can fail fast, use fallback logic, and give the affected service time to recover.

In this guide, you’ll learn what the circuit breaker design pattern is, how its states work, and how to implement it in Java using Spring Boot and Resilience4j.

What Is the Circuit Breaker Pattern in Microservices?

The circuit breaker pattern is a design pattern used in distributed systems to prevent repeated calls to a service that is failing or responding too slowly.

Think of an electrical circuit breaker. When it detects a dangerous condition, it interrupts the flow of electricity. Similarly, a software circuit breaker monitors calls to a remote dependency and temporarily blocks requests when failures cross a configured threshold.

For example, imagine an e-commerce application with separate order, payment, and inventory services. If the payment service becomes unavailable, the order service may continue sending payment requests. Those requests can accumulate, consume threads, and increase pressure on the application.

A circuit breaker can detect repeated failures and stop sending requests temporarily. The application can return a controlled error or execute a fallback instead of waiting for every request to time out.

A circuit breaker does not repair the failing service or automatically route traffic to a healthy instance. Its primary role is to protect system resources, limit repeated unsuccessful calls, and support resilient microservices.

How Does the Circuit Breaker Pattern Work?

A typical circuit breaker has three states: Closed, Open, and Half-Open. The breaker changes states according to configured failure thresholds and recovery rules.

1. Closed State

In the Closed state, requests pass through to the downstream service normally. The circuit breaker records outcomes, such as successful calls, failed calls, and—in configurations that support it—slow calls.

If the measured failure rate exceeds the configured threshold after enough calls have been recorded, the breaker can transition to the Open state.

2. Open State

In the Open state, the circuit breaker rejects calls without attempting the protected remote operation. This behavior is known as failing fast.

The breaker remains open for a configured waiting period. During this time, the application may return a fallback response, display a temporary error, or use suitable cached data when doing so is safe.

3. Half-Open State

After the waiting period, the breaker can enter the Half-Open state. It allows a limited number of test requests to determine whether the downstream service has recovered.

If the test calls succeed according to the configured rules, the breaker returns to Closed. If failures continue, it returns to Open and blocks calls for another waiting period.

circuit breaker pattern in Microservices

How to Implement the Circuit Breaker Pattern in Java with Spring Boot

Spring Boot simplifies the development of stand-alone Java applications and services. You can integrate Resilience4j, a fault-tolerance library that provides circuit breakers, retries, rate limiters, and bulkheads.

The example below uses the Resilience4j Spring Boot 3 integration. Keep your Spring Boot and Resilience4j versions compatible, and use the official documentation when selecting versions for your project.

Step 1: Create a Spring Boot Project

Open Spring Initializr and create a project with these settings:

  • Project: Maven
  • Language: Java
  • Spring Boot: Choose a currently supported Spring Boot 3 version for this example.
  • Dependencies: Spring Web and Spring Boot Actuator

Generate the project, extract the downloaded ZIP file, and open it in IntelliJ IDEA, Eclipse, or VS Code with Java support.

If you already have a Spring Boot project, you can use it instead.

Step 2: Add the Resilience4j Dependencies

In your Maven project’s pom.xml, add the following dependencies inside the <dependencies> section. The example pins Resilience4j to version 2.3.0; verify the current supported version and compatibility before publishing or using the code in production.

<properties>
    <resilience4j.version>2.3.0</resilience4j.version>
</properties>

<dependencies>
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot3</artifactId>
        <version>${resilience4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

If your project already includes Spring Web, Actuator, or other dependencies, avoid adding duplicate entries. Spring Boot’s parent POM or dependency management should manage the Spring Boot dependency versions.

Step 3: Configure the Circuit Breaker

Create or update src/main/resources/application.yml:

resilience4j:
  circuitbreaker:
    instances:
      paymentService:
        slidingWindowType: COUNT_BASED
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3

This configuration creates a circuit breaker named paymentService.

  • slidingWindowType: Evaluates a count-based window of calls.
  • slidingWindowSize: Uses up to 10 calls in the configured window.
  • minimumNumberOfCalls: Requires at least five recorded calls before evaluating the failure rate for a state transition.
  • failureRateThreshold: Sets the failure threshold to 50%.
  • waitDurationInOpenState: Keeps the breaker open for 10 seconds before it can attempt recovery.
  • permittedNumberOfCallsInHalfOpenState: Allows three calls while checking whether the service has recovered.

These are example values, not universal production settings. Tune the circuit breaker configuration to match your service’s traffic, latency, and failure characteristics.

Step 4: Protect a Service Call

Create a PaymentClient.java class. The @CircuitBreaker annotation tells Resilience4j to protect the method call.

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

@Service
public class PaymentClient {

    private final RestTemplate restTemplate;

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

    @CircuitBreaker(
        name = "paymentService",
        fallbackMethod = "paymentFallback"
    )
    public String processPayment() {
        return restTemplate.getForObject(
            "http://localhost:8081/payments",
            String.class
        );
    }

    public String paymentFallback(Exception exception) {
        return "Payment service is temporarily unavailable.";
    }
}

The URL is an example endpoint. Replace it with the actual endpoint for your application.

Next, create AppConfig.java to register the RestTemplate bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

The fallback method must have a compatible return type and parameter signature. For this example, the protected method has no parameters, so the fallback accepts the exception as its only parameter.

The example uses a broad Exception parameter for simplicity. In production, handle failures deliberately and avoid hiding programming errors or unexpected conditions.

Important: A fallback message is not a successful payment. Your application must represent the payment outcome accurately and must not report an unconfirmed transaction as completed. For real payment systems, use appropriate transaction-status checks and idempotency controls.

The example also assumes the downstream payment service is running at the specified URL. If it is unavailable, the request may fail and trigger the fallback. A complete demonstration should test both successful and failing calls.

Step 5: Monitor Circuit Breaker Behavior

Monitoring helps you determine whether the breaker is protecting the application or opening too frequently.

Spring Boot Actuator provides operational endpoints, while Resilience4j can expose circuit breaker metrics and health information when configured with the required integrations.

Track indicators such as:

  • Circuit breaker state transitions.
  • Successful and failed calls.
  • Slow-call and failure rates.
  • Calls rejected while the breaker is open.
  • Fallback frequency and downstream response times.

Secure operational endpoints and expose only the information your application actually needs. Do not publish sensitive management endpoints publicly without appropriate access controls.

Because repeated failures and slow requests can consume database connections and other resources, understanding database connection pooling and timeout configuration can help you build more reliable services.

For further implementation details, see the official Resilience4j documentation and Spring Cloud CircuitBreaker reference.

Circuit Breaker vs. Retry Pattern

The circuit breaker pattern and retry pattern both help applications handle failures, but they solve different problems.

FeatureCircuit breakerRetry pattern
Main purposePrevent repeated calls to an unhealthy dependencyAttempt a failed operation again
Typical behaviorBlocks calls while openRepeats calls according to retry rules
Best suited forPersistent failures and unhealthy dependenciesTemporary or transient failures
Main riskRequests may be rejected while the breaker is openExcessive retries can increase load
Common companionFallbacks and timeoutsBackoff, jitter, and idempotency

A retry can be useful when a failure is temporary, such as a brief network interruption. A circuit breaker is more useful when a dependency is consistently failing or responding too slowly.

They can work together, but configure them carefully. Aggressive retries can amplify traffic and make an already struggling service worse. Use bounded retries, suitable timeouts, and backoff strategies. For operations that may be repeated, consider idempotency to prevent duplicate side effects.

A rate limiter complements a circuit breaker by controlling how many requests a service accepts, helping protect downstream systems from excessive traffic.

Circuit Breaker Best Practices

Follow these circuit breaker best practices to make the pattern useful in production:

  1. Configure realistic thresholds. Set failure rates, minimum call counts, and waiting periods based on actual service behavior rather than copying example values blindly.
  2. Set timeouts. A circuit breaker does not replace a connection or request timeout. Without timeouts, calls may remain blocked for too long.
  3. Design safe fallbacks. Return a clear temporary failure, use cached data only when appropriate, and never pretend that an unconfirmed transaction succeeded.
  4. Monitor state transitions. Measure failures, slow calls, rejected calls, and fallback activity to understand how the breaker behaves under real traffic.
  5. Avoid retry storms. Combine retries with backoff and sensible limits. Make sure retry policies do not overwhelm the downstream service.
  6. Test failure scenarios. Test timeouts, connection failures, repeated errors, recovery, and the behavior of the Half-Open state.
  7. Choose the right dependency boundary. Configure breakers around meaningful remote operations instead of creating unnecessary complexity around every method.

A circuit breaker is not a replacement for load balancing, rate limiting, or bulkheads. These are separate resilience patterns that can complement one another. For example, a bulkhead pattern isolates resources so one overloaded dependency does not consume every available thread or connection.

Frequently Asked Questions

What is the purpose of the circuit breaker pattern in microservices?

The purpose is to prevent repeated calls to a failing or slow dependency, reduce the risk of cascading failures, and give the service time to recover.

When should you use the circuit breaker pattern?

Use it when a service depends on a remote API, database-backed service, or other network dependency that can fail or become slow. It is especially useful when repeated requests can consume significant resources or affect other services.

How do you implement a circuit breaker pattern in Java?

You can implement it with a fault-tolerance library such as Resilience4j. In a Spring Boot application, add the integration dependency, configure the breaker, and protect a method with the @CircuitBreaker annotation and an appropriate fallback.

What is the difference between a circuit breaker and a retry?

A retry attempts an operation again after a failure. A circuit breaker temporarily blocks calls when failure conditions indicate that a dependency may be unhealthy. Use retries for suitable transient failures and circuit breakers to limit repeated calls to failing dependencies.

Conclusion

The circuit breaker pattern in microservices is an important technique for building resilient distributed systems. By managing the Closed, Open, and Half-Open states, an application can stop repeatedly calling an unhealthy dependency and reduce the impact of failures.

With Spring Boot and Resilience4j, you can configure a breaker, protect remote service calls, and implement fallback behavior without building the entire mechanism from scratch. Combine it with sensible timeouts, monitoring, carefully controlled retries, and realistic testing to improve application reliability.

 

 

 

 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top