This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Cloud Commons 4.3.0! |
Spring Cloud Circuit Breaker
Spring Cloud Circuit breaker provides an abstraction across different circuit breaker implementations. It provides a consistent API to use in your applications, letting you, the developer, choose the circuit breaker implementation that best fits your needs for your application.
Core Concepts
To create a circuit breaker in your code, you can use the CircuitBreakerFactory
API.
When you include a Spring Cloud Circuit Breaker starter on your classpath, a bean that implements this API is automatically created for you.
The following example shows a simple example of how to use this API:
@Service
public static class DemoControllerService {
private RestTemplate rest;
private CircuitBreakerFactory cbFactory;
public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
this.rest = rest;
this.cbFactory = cbFactory;
}
public String slow() {
return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
}
}
The CircuitBreakerFactory.create
API creates an instance of a class called CircuitBreaker
.
The run
method takes a Supplier
and a Function
.
The Supplier
is the code that you are going to wrap in a circuit breaker.
The Function
is the fallback that is run if the circuit breaker is tripped.
The function is passed the Throwable
that caused the fallback to be triggered.
You can optionally exclude the fallback if you do not want to provide one.
Circuit Breakers In Reactive Code
If Project Reactor is on the class path, you can also use ReactiveCircuitBreakerFactory
for your reactive code.
The following example shows how to do so:
@Service
public static class DemoControllerService {
private ReactiveCircuitBreakerFactory cbFactory;
private WebClient webClient;
public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) {
this.webClient = webClient;
this.cbFactory = cbFactory;
}
public Mono<String> slow() {
return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform(
it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback")));
}
}
The ReactiveCircuitBreakerFactory.create
API creates an instance of a class called ReactiveCircuitBreaker
.
The run
method takes a Mono
or a Flux
and wraps it in a circuit breaker.
You can optionally profile a fallback Function
, which will be called if the circuit breaker is tripped and is passed the Throwable
that caused the failure.
Configuration
You can configure your circuit breakers by creating beans of type Customizer
.
The Customizer
interface has a single method (called customize
) that takes the Object
to customize.
For detailed information on how to customize a given implementation see the following documentation:
Some CircuitBreaker
implementations such as Resilience4JCircuitBreaker
call customize
method every time CircuitBreaker#run
is called.
It can be inefficient.
In that case, you can use CircuitBreaker#once
method.
It is useful where calling customize
many times doesn’t make sense, for example, in case of consuming Resilience4j’s events.
The following example shows the way for each io.github.resilience4j.circuitbreaker.CircuitBreaker
to consume events.
Customizer.once(circuitBreaker -> {
circuitBreaker.getEventPublisher()
.onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition()));
}, CircuitBreaker::getName)
Spring Interface Clients Support
Spring Cloud provides support for Spring Interface Clients integration through the following configurers:
-
CircuitBreakerRestClientHttpServiceGroupConfigurer
-
CircuitBreakerWebClientHttpServiceGroupConfigurer
These configurers enable CircuitBreaker support for Spring Interface Client Groups.
When fallback classes are configured using the @HttpServiceFallbackAnnotation
,
CircuitBreaker adapter decorators are added:
- CircuitBreakerAdapterDecorator
is used with RestClient
- ReactiveCircuitBreakerAdapterDecorator
is used with WebClient
You can disable CircuitBreaker integration for HTTP service clients by setting the appropriate property:
This prevents CircuitBreaker decorators from being applied to interface-based HTTP client groups. |
Declaring Fallbacks with Annotations
Fallbacks are configured using the @HttpServiceFallback
annotation on configuration classes.
This annotation allows you to declare:
-
The fallback implementation class (via
value
) -
The service interfaces the fallback supports (via
forService
, optional) -
The group the fallback applies to (via
forGroup
, optional)
Multiple @HttpServiceFallback
annotations can be declared on the same class using Java’s @Repeatable
annotation mechanism.
If no group is specified, the fallback applies to all groups that do not have an explicit per-group fallback for the given service interfaces.
Fallback classes are resolved using the following precedence:
-
A fallback class with both matching
forService
andforGroup
-
A fallback class with matching
forService
and noforGroup
(global fallback for service) -
A fallback class with no
forService
orforGroup
(default for all services in group or globally)
Example
@HttpServiceFallback(value = DefaultFallbacks.class)
@HttpServiceFallback(value = GroupAndServiceSpecificFallbacks.class, service = {BillingService.class, ShippingService.class}, group = "billing")
public class MyFallbackConfig {
...
}
This configuration results in:
-
DefaultFallbacks
used as a global fallback for all services not explicitly handled -
GroupAndServiceSpecificFallbacks
used only forBillingService
andShippingService
within the"billing"
group
|
How CircuitBreaker Adapters Work
The adapters wrap @HttpExchange
method calls with CircuitBreaker logic.
When a fallback is triggered, a proxy is created using the user-defined fallback class.
The appropriate fallback method is selected by matching:
-
A method with the same name and parameter types, or
-
A method with the same name and parameter types preceded by a
Throwable
argument (to access the cause of failure)
Given the following interface:
@HttpExchange("/test")
public interface TestService {
@GetExchange("/{id}")
Person test(@PathVariable UUID id);
@GetExchange
String test();
}
A matching fallback class could be:
public class TestServiceFallback {
public Person test(UUID id);
public String test(Throwable cause);
}