Complete microservices architecture with real examples — Step 5 — Circuit breaker with hystrix
Hi, guys! Welcome to Agnasarp! Today we came here with a very useful concept in the microservices architecture. In the home grid, there is a switch called “breaker” or “circuit breaker” which is automatically switched off while the electricity is flowing off. It will secure our home and everything not letting a bad thing happened. Likewise, we need to have a mechanism to secure our whole microservices echo system without hanging any service because definitely there may be service calls from one microservice to others. Therefore the same concept of circuit braking can be applied to this echo system. Here we have picked up Hystrix from several technologies. We have to setup Hystrix dashboard to see graphically what is going on behind the scene and configure the API gateway to support Hystrix.
Step 5 — Hystrix dashboard
Let’s take a look at the project initialization with https://start.spring.io/
- Project build tool: Maven
- Language: Java
- Spring boot: 2.3.10 (SNAPSHOT)
- Project Metadata
Group: com.agnasarp
Artifact: agnasarp-hystrix-dashboard
Name: agnasarp-hystrix-dashboard
Description: Agnasarp Hystrix Dashboard
Package name: com.agnasarp.hystrixdashboard
Packaging: Jar
Java version: 8
- Dependencies:
Eureka Discovery Client: A REST based service for locating services for the purpose of load balancing and failover of middle-tier servers.
Hystrix Dashboard [Maintenance]: Circuit breaker dashboard with Spring Cloud Netflix Hystrix. In maintenance mode with no direct replacement.
Here, we have to pick the right Spring Boot version because Hystrix may not support for few higher versions of Spring Boot. Then, we will import the project into IntelliJ Idea.
Project structure
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>2.3.10.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.agnasarp</groupId>
<artifactId>agnasarp-hystrix-dashboard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>agnasarp-hystrix-dashboard</name>
<description>Agnasarp Hystrix Dashboard</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.BUILD-SNAPSHOT</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-netflix-hystrix-dashboard</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>${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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
application.yml
server:
port: 8480
spring:
application:
name: HYSTRIX-DASHBOARD
hystrix:
dashboard:
proxy-stream-allow-list: "*"
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
hostname: localhost
AgnasarpHystrixDashboardApplication.java
package com.agnasarp.hystrixdashboard;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableEurekaClient
@EnableHystrixDashboard
public class AgnasarpHystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(AgnasarpHystrixDashboardApplication.class, args);
}
}
Now we have to do few modifications in API Gateway microservice as well. Those changes are highlighted below.
API Gateway modifications
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.3.10.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.agnasarp</groupId>
<artifactId>agnasarp-cloud-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>agnasarp-cloud-gateway</name>
<description>Agnasarp Cloud Gateway</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.BUILD-SNAPSHOT</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<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-netflix-hystrix</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>${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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
application.yml
server:
port:
9191
spring:
application:
name: API-GATEWAY
cloud:
gateway:
routes:
- id: DEPARTMENT-SERVICE
uri: lb://DEPARTMENT-SERVICE
predicates:
- Path=/departments/**
filters:
- name: CircuitBreaker
args:
name: DEPARTMENT-SERVICE
fallbackuri: forward:/departmentServiceFallback
- id: USER-SERVICE
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
filters:
- name: CircuitBreaker
args:
name: USER-SERVICE
fallbackuri: forward:/userServiceFallback
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
management:
endpoints:
web:
exposure:
include: hystrix.stream
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
hostname: localhost
AgnasarpCloudGatewayApplication.java
package com.agnasarp.cloudgateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class AgnasarpCloudGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(AgnasarpCloudGatewayApplication.class, args);
}
}
And a new fallback controller has been added to forward the request when the 3rd party API is not available or unresponsive within the timeout defined in the application.yml file and the fallback method in the controller will give a meaningful message as below.
FallbackController.java
package com.agnasarp.cloudgateway.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FallbackController {
@GetMapping("/departmentServiceFallback")
public String departmentServiceFallback(){
return "DEPARTMENT-SERVICE is not available or unresponsive. Please try again later.";
}
@GetMapping("/userServiceFallback")
public String userServiceFallback(){
return "USER-SERVICE is not available or unresponsive. Please try again later.";
}
}
If Service Registry, Department Service, User Service, API Gateway Service, and Hystrix Dashboard Service is up and running we can test whether our circuit breaker is working well or not. We can go to the Hystrix Dashboard with the below link.
We can see the status of microservices configured in API Gateway via the below hystrix stream endpoint.
http://localhost:9191/actuator/hystrix.stream
Hystrix Dashboard will show the circuit as closed as below.
Now we have down the Department Service which is being called by the User Service and try to call the User Service, so we got the below response hardcoded in the fallback method userServiceFallback.
Then the Hystrix Dashboard shows the status of our services graphically when the circuit opens.
That is all for today. In summary, we have created a Hystrix Dashboard and configured API Gateway to support Hystrix to implement the circuit breaker pattern in our echo system. You can also try with these and all source code can be downloaded from GitHub using the below links. Hope you have grabbed something from this post and we will meet again with another important concept in microservices. Until then, bye bye!
Download source from github:
Go to Step 1 — Department Microservice
Go to Step 2 — User Microservice
Go to Step 3 — Service Registry
Go to Step 4 — API Gateway
Originally published at https://www.agnasarp.com on April 2, 2021.