Spring Cloud + Hysterix Circuit Breaker (2024) with Example
In this tutorial we will be implementing Spring Boot with Hysterix Circuit Breaker with the help of example.In an enterprise application, couple of services work as a team for the smooth working of an application.
There may be cases that a service fails and the result can lead to the failure of the whole application. So here comes the role of Hysterix Circuit Breaker.
If any of services is not working in an enterprise application, the execution is redirected to (fallback) to a diverse path automatically can be implemented by the circuit breaker. Netflix provide a tool known as Hysterix for this mechanism.
The Hysterix can be explained as follows:-
In this example, we have a eureka server, a HysterixDemoServer and a HysterixDemoClient. We are calling an API of HysterixDemoServer from the HysterixDemoClient. There would be a different behaviour seen in case the HysterixDemoServer is down.
-
EurekaServer
All the services can register their address(hosts and port number) to the eureka server. This will be the standard directory layout for maven project structure-
We need to start by creating a Maven pom.xml(Project Object Model) file. The pom.xml file contains the project configuration details.<?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>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.codeusingjava.Hysterix</groupId> <artifactId>HysterixEurekaServer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-service</name> <description>Demo project for Spring Boot Eureka register service</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>0</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>
Creating the configuration in application.yml file as follows-
We can externalise our configuration with the help of this file.eureka: client: registerWithEureka: false fetchRegistry: false server: waitTimeInMsWhenSyncEmpty: 0
Creating the configuration in application.properties file as follows-spring.application.name=HysterixEurekaService server.port=8761
The main class for HysterixEurekaServer is as follows-package com.codeusingjava.Hysterix; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class HysterixEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(HysterixEurekaServerApplication.class, args); } }
HysterixDemoServer
It is the simple service which executes the api mentioned in the controller.
The api present in the controller of this application would be accessed by thr HysterixDemoClient service.
The Maven project structure is a follows:-
<?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>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.codeusingjava.Hysterix</groupId> <artifactId>HysterixDemoServer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-server</name> <description>Demo project for Spring Boot server microservice</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>0</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>
Creating the configuration in application.yml filespring: application: name: HysterixDemoServer server: port: 8088 eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost
package com.codeusingjava.Hysterix.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/server") public class HelloController { @GetMapping public String message(){ return "Welcome to Hysterix Demo Server"; } }
Creating the main class
@EnableDiscoveryClient enables the service discovery and such functionalities.package com.codeusingjava.Hysterix; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class DemoServerApplication { public static void main(String[] args) { SpringApplication.run(DemoServerApplication.class, args); } }
HysterixDemoClient
It is another service registered with the EurekaServer.
It is the client service where the API of HysterixDemoServer is to be consumed. The project structure for the HysterixDemoClient is as follows-
In the pom.xml, we need to add all the dependencies that we added in HysterixDemoServer.
Now add the following dependency also in the pom.xml file.<?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>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.codeusingjava.Hysterix</groupId> <artifactId>HyserixDemoClient</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-client</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>0</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>
spring: application: name: HysterixDemoClient server: port: 8089 eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost
The controller class for the HysterixDemoClient is as follows-
@HysterixCommand is defined on the method where we need to apply the fallbackMethod.
The fallbackMethod which is in our program is handleFallback() will be executed when HysterixDemoServer service is not in an active state.package com.codeusingjava.Hysterix.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/api/client") public class DemoController { @Autowired public RestTemplate restTemplate; @GetMapping @HystrixCommand(fallbackMethod = "handleFallback") public String test(){ String url = "http://HysterixDemoServer/api/server"; return restTemplate.getForObject(url, String.class); } public String handleFallback(){ return "This service is fallback service. It is called when HysterixDemoServer is down"; } }
@EnableCircuitBreaker defines that the functionality such that circuit breaker is enabled automatically in the application.package com.codeusingjava.Hysterix; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class DemoClientApplication { public static void main(String[] args) { SpringApplication.run(DemoClientApplication.class, args); } } @Configuration class DemoClienConfiguration{ @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
Run the application
If we now run the HysterixEurekaServerApplicationNow run the HysterixDemoServer as spring boot application
Now run the HysterixDemoClient as spring boot application The output is as follows:-
Go to console at localhost:8761
Now click the url of the HysterixDemoClient. Now type the following url:- http://localhost:8089/api/client The output is as follows-
Now Purposely stop the HysterixDemoServer service and again refresh the above url. The output is as follows:-