Using Spring Cloud Gateway MVC and Spring Boot, I have configured a route to a microservice that has multiple instances which are load balanced using a Eureka discovery service. I then added a circuit breaker filter for resilience. I've elected to use the Java-based config.
I set up two microservices - one that will always succeed and one that will always fail (timeout). The incoming requests are routed to one of those two services using the load balancer (round-robin). Half the requests succeed, and half fail (timeout). The circuit breaker then opens because of the bad service and blocks traffic to both the good and bad service. I figure this is because the circuit breaker opens for the whole route instead of only on the one load balanced service instance.
How would I configure the route so that the circuit breaker blocks only the bad service instance and continues to route all traffic to the good service instance?
- Spring cloud version 2023.0.0
- Circuit Breaker is resilience4j (artifactId: spring-cloud-starter-circuitbreaker-resilience4j)
- Load Balancer is Eureka (artifactId: spring-cloud-starter-netflix-eureka-client)
@Bean
public RouterFunction<ServerResponse> serviceRouteConfig() {
return RouterFunctions
.route()
.route(GatewayRequestPredicates.path("/api/service/**"), HandlerFunctions.http())
.before(BeforeFilterFunctions.stripPrefix(2)) // remove "/api/service/"
.filter(LoadBalancerFilterFunctions.lb("SERVICE-NAME"))
.filter(CircuitBreakerFilterFunctions.circuitBreaker("defaultCircuitBreaker"))
.build();
}
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCircuitBreakerCustomizer() {
return factory -> factory
.configureDefault(
id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(
CircuitBreakerConfig.custom()
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.slidingWindowSize(5)
.minimumNumberOfCalls(5)
.build())
.timeLimiterConfig(TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(2)).build())
.build());
}