Circuit Breaker using Spring Boot Hystrix
Hystrix is a library from Netflix that implements circuit breaker pattern. It is a latency and fault tolerance library for isolating points of access to remote systems, services or third party libraries. Thus it stops cascading failures and enables resilience in complex distributed systems.
Fallback – If a service call fails or takes too long to respond, Hystrix can provide a fallback response. It allows the application to degrade gracefully rather than failing right away.
In the below example, we will implement Hystix. We will create one microservice which calls another. However, we will keep another service down so that circuit breaker pattern will take place and fallback response will be given instead of an error message.
Create a spring starter project in STS
pom.xml – Make sure the version of spring-boot-starter-parent, java, spring-cloud.version remain same as given in the below pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.sks</groupId>
<artifactId>circuit-breaker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>circuit-breaker</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Springboot application class
package com.sks;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableHystrix
public class CircuitBreakerApplication {
public static void main(String[] args) {
SpringApplication.run(CircuitBreakerApplication.class, args);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
ExternalService.java
package com.sks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class ExternalService {
@Autowired
private RestTemplate restTemplate;
public String callExternalApi() {
String result = restTemplate.getForObject("http://localhost:8080/api/hello1", String.class);
return result;
}
}
ApiController.java
package com.sks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@RestController
public class ApiController {
@Autowired
private ExternalService externalService;
@GetMapping("/test")
@HystrixCommand(fallbackMethod = "fallbackResponse",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")})
public String testCircuitBreaker() {
return externalService.callExternalApi();
}
public String fallbackResponse(Throwable t) {
if (t instanceof HttpClientErrorException) {
HttpClientErrorException ex = (HttpClientErrorException) t;
if (ex.getStatusCode() == HttpStatus.NOT_FOUND) {
return "Resource not found";
}
}
return "Fallback response due to failure. Unable to connect to the backend service right not."
+ " Please try again later.";
}
}