Have you ever faced problems consuming third party APIs? If so, using circuit breaker resilience can help you.
Context
Imagine that your application needs to call another API, but the other API is unstable. You keep calling the API but still get no answer. In this case, you can configure a circuit breaker, which is responsible for cutting the connection with the problematic API for a while, avoiding unnecessary and repetitive calls to a service that is unavailable.
Setting up a circuit breaker means your service can return a standard, pre-configured answer, and after a pre-set time, the mechanism will evaluate the connection; if the connection is better, the circuit breaker will allow connections again.
What is a Circuit Breaker?
Circuit breakers are based on three states:
- CLOSED: when everything is functioning properly, the requests are forwarded to the service
- OPEN: after the application gets successive errors or timeouts, the mechanism closes the connection and no longer sends the requests to the service
- HALF-OPEN: a transitional state when the mechanism is evaluating whether the service is stable again by sending some requests and checking the response
Image courtesy of Resilience4j
The animation below illustrates a circuit breaker in action:
Image courtesy of codecentric. Available at media.graphassets
Case Study
To explain the use of circuit breakers, we will implement a solution using resilience4j in a Java Spring Boot application.
Spring Boot 2 already provides a starter for resilience:
org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j
So we will add this dependency on pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
To configure the parameters, add this on your bootstrap.yml:
resilience4j.circuitbreaker:
configs:
default:
failureRateThreshold: 25 # Percentage (%) of calls accepted with failure.
slowCallRateThreshold: 25 # Percentage (%) of calls accepted with delay.
slowCallDurationThreshold: 5000 # In milliseconds, maximum time of a call in a sincron API.
permittedNumberOfCallsInHalfOpenState: 5 # Number of calls to evaluate if the circuit can close again.
slidingWindowType: COUNT_BASED
slidingWindowSize: 5 # Window size to evaluate the Thresholds.
minimumNumberOfCalls: 2 # minimum number of calls to evaluate the Thresholds
waitDurationInOpenState: 30000 # In milliseconds, time wich the circuit will be open to evaluate if it is possible to close the circuit again.
ignoreExceptions: # Here you can add the exception you want to ignore on your metrics
- org.springframework.web.client.HttpClientErrorException # Just a example
You can also check the documentation for more information.
Config Class Example
Let's look at an example. In this case, we'll first log the information that the circuit breaker state has changed:
@Configuration
@Slf4j
public class CircuitBreakerConfig {
@Bean
public RegistryEventConsumer<CircuitBreaker> consumer() {
return new RegistryEventConsumer<CircuitBreaker>() {
@Override
public void onEntryAddedEvent(EntryAddedEvent<CircuitBreaker> entryAddedEvent) {
entryAddedEvent.getAddedEntry()
.getEventPublisher()
.onStateTransition(
//You can add something you want to do when happened some state change
//EG: log the change
event -> log.info(event.toString())
);
}
@Override
public void onEntryRemovedEvent(EntryRemovedEvent<CircuitBreaker> entryRemoveEvent) {
//from abstract implementation
}
@Override
public void onEntryReplacedEvent(EntryReplacedEvent<CircuitBreaker> entryReplacedEvent) {
//from abstract implementation
}
};
}
}
Below is an example of how to use a fallback method to return something when the circuit is open or some error occurs when the request is sent.
Note: the object named service is responsible for calling the API.
@RestController
@RequestMapping(value = "/clients")
@Slf4j
public class ClientController {
private final ClientsService service;
public ClientController(ClientsService service) {
this.service = service;
}
@CircuitBreaker(name = "returnClientData", fallbackMethod = "returnClientDataFallback")
@GetMapping(value = "/{socialNumber}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<ClientDataDTO> returnClientData(@PathVariable @Valid String socialNumber) {
return ResponseEntity.ok(service.clientData(socialNumber));
}
public ResponseEntity<ClientDataDTO> returnClientDataFallback(String socialNumber, Exception e) {
log.debug(MessageFormat.format("Error to communicate to API Clients controller: socialNumber:{0} {1}", socialNumber, e));
return ResponseEntity.ok(ClientDataDTO.builder()
.name("John doe")
.build());
}
}
Conclusion
In this article, you learned how to avoid problems consuming APIs by using a circuit breaker, how to configure it in your Spring Boot Application, and also how to create a fallback method to return a standard response.
Ready to try it in your project? Let me know how it goes in the comments below!
References
Author
Ederson Silva
Ederson Silva is a Software Engineer at Avenue Code. He has 6 years of experience with Java, working with web applications in MVC architecture and microservices. He is also passionate about traveling, learning about new things and living new experiences.