Why Circuit Breakers?
1. Failing fast – It’s better to fail fast than trying to establish connection and fail later.
2. Fallback functionality
3. Automatic recovery
How it works
1. Detect something is wrong.
2. Take temporary steps to avoid the situation getting worse
3. Deactivate the problem component so it doesn’t affect downstream components
Circuit Breaker Pattern
1. When to break circuit
2. What to do when circuit breaks
3. When to resume requests
Circuit Breaker Parameters
Before knowing Circuit Breaker parameters, let’s understand how request flow works.
As per above diagram, we have received total 7 requests out of those 3 got succeeded and 4 either timed out or throws an error.
Configuring when does the circuit trip?
1. Last n requests to consider for the decision
2. How many of those should fail?
3. Timeout duration
When does a circuit get back to normal?
1. How long a circuit trip to try again? – Retrying after circuit break
Example:
Last n requests to consider for the decision: 7
How many of those should fail? 3
Timeout duration: 2s
How long to wait (Sleep window): 10s
OK!
So now we have understood the theory of Circuit Breakers it’s time to implement. To implement Circuit Breaker pattern we have technology called Hystrix (provided by Netflix). Rest of the article focuses on setting up Hystrix in Spring Boot microservices.
What is Hystrix?
1. Open source library originally created by Netflix
2. Implements Circuit breaker pattern so you don’t have to
3. Give this configuration params and it does the work
4. Works well with Spring Boot
Steps to Add Hystrix to Spring Boot Microservices
Step 1: Add the below starter dependency in pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
Step 2: Go to main class of your microservice and add @EnableCircuitBreaker annotation
package org.techlearnings; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class UserDetailsServiceApplication { public static void main(String[] args) { SpringApplication.run(MovieCatalogServiceApplication.class, args); } @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
Step 3: Add below annotation to the methods that need circuit breakers.
@HystrixCommand
The way @HystrixCommand works is, it creates the proxy and breaks the circuit by not calling the method available in actual class based on the configuration (circuit breaker parameters) you provided.
Step 4: Add Circuit Breaker parameters
Example
@HystrixCommand(commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000") }) public UserDetails getUserDetails() { return restTemplate.getForObject("http://user-details-service/userdetails/" + 1, UserDetails.class); }
Great! Now we have configured Circuit Breaker in our microservices. But what if requests keep coming for the services we have setup Circuit Breakers for? Well! We need Fallback mechanism in that case.
How Hystrix works?
Let’s say you have 3 microservices named app1, app2 and app3
One of the resources(r1) in app1 has a method named m1, which is consuming resources available in app2 and app3. If you annotate m1 with @HystrixCommand then Hystrix creates the proxy of r1. So if you consume 2 or more microservices from the same method then you cannot handle the fallback. So better, refactor it by creating the method(annotated with @HystrixCommand) in separate bean.
Hystrix Properties
@HystrixCommand(commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000") }) public UserDetails getUserDetails() { return restTemplate.getForObject("http://user-details-service/userdetails/" + 1, UserDetails.class); }