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.

Hystrix

Step 5 — Hystrix dashboard

Let’s take a look at the project initialization with https://start.spring.io/

Spring Initialzr — Hystrix Dashboard
  • 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

Project structure of hystrix dashboard in IntelliJ Idea

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.

http://localhost:8480/hystrix

Hystrix Dashboard

We can see the status of microservices configured in API Gateway via the below hystrix stream endpoint.

http://localhost:9191/actuator/hystrix.stream

Hystrix Stream
User Service — Success Scenario

Hystrix Dashboard will show the circuit as closed as below.

Hystrix Dashboard — Circuit Closed

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.

User Service — Fail Scenario

Then the Hystrix Dashboard shows the status of our services graphically when the circuit opens.

Hystrix Dashboard — Circuit Opened

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:

Download hystrix-dashboard

Download api-gateway

Download service-registry

Download department-service

Download user-service

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.

Agnasarp is a technology-focused blog that has enough information about cutting-edge technologies that you can use for your problems. Stay with us!

Agnasarp is a technology-focused blog that has enough information about cutting-edge technologies that you can use for your problems. Stay with us!