Circuit Breaker: Handling Unreachable Services in Spring Boot
Stop cascading failures when a downstream microservice goes offline. Wrap unreachable calls in a Resilience4j circuit breaker with fallbacks, timeouts and graceful degradation.
▶ Watch this tutorial on MasterLabSystems on YouTube — and subscribe for more.
Introduction
When one microservice goes down, naive HTTP clients hang on the TCP timeout — 30, 60 seconds per call. That thread sits blocked, the connection pool drains, and within minutes the *caller* dies too. This is a cascading failure, and it's the single most common outage pattern in distributed systems.
A circuit breaker stops it. This tutorial shows how to wrap unreachable-service calls in Spring Boot using Resilience4j, with fallbacks and aggressive timeouts.
Who this is for
Java backend engineers running two or more services in production who have ever woken up at 3 a.m. because "service A is slow → service B is slow → everything is down."
The three states of a circuit breaker
- CLOSED — normal. Calls pass through. Failures are counted.
- OPEN — too many failures. Calls fail fast (no network attempt) and fallback runs.
- HALF_OPEN — after a wait, a few probe calls go through. Success → CLOSED. Failure → OPEN again.
Step 1 — Add Resilience4j
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Step 2 — Configure the breaker
resilience4j:
circuitbreaker:
instances:
paymentService:
slidingWindowSize: 10
failureRateThreshold: 50
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
timelimiter:
instances:
paymentService:
timeoutDuration: 2s
If 5 of the last 10 calls fail, the breaker opens for 10 seconds. Every individual call has a 2-second hard timeout.
Step 3 — Annotate the unreachable call
```java
@Service
public class PaymentClient {private final RestClient rest = RestClient.create("http://payment-service");
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback") @TimeLimiter(name = "paymentService") public CompletableFuture<PaymentResponse> charge(ChargeRequest req) { return CompletableFuture.supplyAsync(() -> rest.post().uri("/charge").body(req).retrieve().body(PaymentResponse.class) ); }
private CompletableFuture<PaymentResponse> paymentFallback(ChargeRequest req, Throwable t) { return CompletableFuture.completedFuture( PaymentResponse.queued(req.id()) ); } } ```
The fallback signature must match the original method plus a trailing Throwable.
How this behaves when payment-service is down
1. First few calls time out at 2s and increment the failure counter. 2. Breaker flips to OPEN. Subsequent calls return the fallback *instantly* — no network attempt. 3. After 10s the breaker enters HALF_OPEN and lets 3 calls probe. 4. If payment-service is healthy again, breaker closes and traffic resumes.
Your callers never block longer than 2 seconds, even during a 1-hour outage.
Step 4 — Expose metrics
management:
endpoints.web.exposure.include: health,metrics,circuitbreakerevents
metrics.tags:
application: ${spring.application.name}
Scrape /actuator/prometheus and alert on resilience4j_circuitbreaker_state{state="open"} == 1 for more than 60 seconds.
Common errors and fixes
- Fallback never fires — Resilience4j wraps the method via AOP. Self-invocation bypasses the proxy. Call the annotated method from a *different* bean.
- Breaker never opens — your call returns 200 with an error body. Resilience4j only counts thrown exceptions; map error responses to exceptions in the RestClient.
TimeLimiternot applied — only works withCompletableFutureor reactive types. A plain synchronous return type is ignored.- State leaks across tenants — one breaker instance covers all calls. For per-tenant isolation, use a
CircuitBreakerRegistryand create one breaker per tenant key.
Source code
Full demo on GitHub with a "chaos" endpoint that returns intermittent 503s and a Grafana dashboard showing state transitions live.
Related tutorials
- [Circuit Breaker with Resilience4j in Spring Boot](/blog/circuit-breaker-resilience4j-spring-boot)
- [Spring Boot Retry with Resilience4j](/blog/spring-boot-retry-with-resilience4j)
- [Spring Boot Microservices Architecture](/blog/spring-boot-microservices-architecture-explained)
<!-- AFFILIATE-SECTIONS-V1 -->
Tools used in this tutorial
The setup below uses the following tools. Versions matter less than the role each one plays, so swap freely as long as you keep the responsibilities the same.
- Resilience4j
- Circuit Breaker
- Spring Boot
- Fault Tolerance
- Spring Cloud
- Docker
- Kubernetes
Real-world production context
In production, a microservices stack like this rarely runs on a single laptop or one bare VM. Teams ship each service as a container image, push it to a registry, and roll it out across a cluster so that one service can scale (or fail) without dragging the others down. Service discovery, config servers and gateways usually sit behind a managed load balancer, with TLS terminated at the edge and internal traffic restricted to a private network. The reason cloud hosting becomes a hard requirement is operational: you need health checks, autoscaling, rolling deploys, log aggregation and a metrics backend just to keep the system honest. Most engineering teams reach for a managed Kubernetes service or a container platform on AWS, GCP or DigitalOcean so they can focus on services rather than on patching nodes.
Recommended tools & deployment options
Once the tutorial works on your machine, the next question is *where do I run this for real?* These are the platforms most backend teams reach for, and the trade-offs between them:
- [DigitalOcean](https://www.digitalocean.com/) — the simplest path from a working container to a public URL. App Platform deploys directly from a Dockerfile, managed databases and Redis are one click away, and pricing is predictable. A common way to deploy the setup in this tutorial is using a cloud provider like DigitalOcean when you want to ship quickly without learning a full cloud SDK.
- [AWS](https://aws.amazon.com/) — the default for enterprise workloads. ECS Fargate or EKS run containers without you managing servers, RDS handles the database, and CloudWatch covers logs and metrics. In production environments, developers typically host this on AWS or a similar cloud platform when they need fine-grained IAM, multi-region failover, or deep integration with other AWS services.
- Docker — the packaging format every modern deploy target understands. Build once, run the same image locally, in CI and in production.
- Kubernetes (managed: EKS, DOKS, GKE) — the right choice once you have more than a handful of services, need rolling updates, autoscaling and policy-driven networking. For a single service it is usually overkill; for a microservices estate it quickly pays for itself.
A VPS or managed cloud service is required to run this architecture end-to-end — the local docker-compose setup is for development, not for serving traffic.
Next steps & related tutorials
Keep the momentum going with the next tutorial in this learning path:
- [Next: Spring Boot Microservices Architecture Explained Step by Step](/blog/spring-boot-microservices-architecture-explained)
- [Next: How to Build a Spring Cloud Config Server](/blog/spring-cloud-config-server-tutorial)
- [Next: Service Discovery with Eureka in Spring Boot](/blog/service-discovery-with-eureka)
- [Next: Circuit Breaker in Spring Boot with Resilience4j — Protect Your System from Overload](/blog/circuit-breaker-resilience4j-spring-boot)
Source Code
Get the full project on GitHub
More From the Channel
Follow the full tutorial series on YouTube
The MasterLabSystems channel publishes in-depth, project-based tutorials on Java, Spring Boot, microservices, Docker, Kubernetes, AWS and DevOps — the same topics covered on this site, with full code walkthroughs.
Stay in the Loop
Get the next tutorial in your inbox
next tutorial →
Build Real-Time Apps with Spring Boot and Kafka: A Masterclass
Related tutorials
Spring Boot Microservices Architecture Explained Step by Step
A complete, beginner-friendly walkthrough of microservices architecture using Spring Boot — services, gateway, discovery, config and observability.
How to Build a Spring Cloud Config Server
Step-by-step guide to building a centralized configuration server with Spring Cloud Config, Git-backed properties and dynamic refresh.
Service Discovery with Eureka in Spring Boot
How service discovery works, why you need it, and how to set up Netflix Eureka with Spring Cloud step by step.