June 29, 2022

Spring Boot Microservices + Hystrix Circuit Breaker

In this post we’ll see how to configure Netflix Hystrix fault tolerance library along with Spring Boot microservice. Hystrix library provides an implementation of the circuit breaker pattern using which you can apply circuit breakers to potentially failing method calls. You can also specify a fallback method to which the failed method calls are redirected by Hystrix.

How does Hystrix circuit breaker work

Spring Cloud Netflix Hystrix looks for any method annotated with the @HystrixCommand annotation. Such method is wrapped in a proxy connected to a circuit breaker so that Hystrix can monitor it.

If the remote call to the actual method fails, Hystrix calls the fallback method. For example-

@GetMapping(value="/{id}")
@HystrixCommand(fallbackMethod = "defaultAccounts")
public List<Account> showEmployees(@PathVariable("id") String id) {
  ...
  ...
}

If calls to showEmployees() keep on failing because of any reason such as network issues, Hystrix opens a circuit on showEmployees() method and falls back on the fallback method to at least show something to the user rather than displaying nothing or a stack trace of underlying exception.

Spring Boot Microservice with Hystrix example

We’ll take the same example used in this post Spring Boot Microservices example as base and make changes to configure Hystrix.

In the example there are two Microservices User and Account and from User there is a call to Account to get account details for the passed Id. In the application Eureka is used for registering services and for service discovery.

Changes for Hystrix circuit breaker

There are no changes required in Spring Boot project for Eureka Server as well as for Account service app.

In the User Spring Boot project dependency for Hystrix has to be added and following Rest Controller class and Service class has to be used.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

UserController class

There is a method showEmployees which in turn calls the method in the UserService class.

import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
  @Autowired
  private UserService userService;

  @GetMapping(value="/{id}")
  public List<Account> showEmployees(@PathVariable("id") String id) {
    //System.out.println(id);       
    List<Account> accounts = userService.showEmployees(id);
    return accounts;        	
  }
}

UserService class

From the showEmployees() method the Account microservice is called to get all the associated accounts for the passed employee ID. For making a remote call RestTemplate instance is used.

ShowEmployees() method is annotated with @HystrixCommand annotation which also specifies a fallback method (defaultAccounts) to be used in case of failure to call the remote microservice.

Fallback method defaultAccounts() returns an empty list.

import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class UserService {
  @Autowired
  private RestTemplate restTemplate;

  @HystrixCommand(fallbackMethod = "defaultAccounts")
  public List<Account> showEmployees(@PathVariable("id") String id) {
    System.out.println(id);
//  List<Account> accounts = new RestTemplate().exchange(
//    "http://localhost:9000/accounts/{empId}", HttpMethod.GET, null, new
//     ParameterizedTypeReference<List<Account>>(){}, id).getBody();
        
    List<Account> accounts = restTemplate.exchange(
    "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new
    ParameterizedTypeReference<List<Account>>(){}, id).getBody();
    for(Account acct : accounts) {
      System.out.println(acct.getEmpId());
      System.out.println(acct.getAccountId());
      System.out.println(acct.getBranch());
    }
    return accounts;        	
  }
    
  // Fall back method used in case circuit is opened
  public List<Account> defaultAccounts(String id) {
    return Collections.emptyList();
  }
}
SpringBootUserApplication class

Application Class with main method is annotated with @SpringCloudApplication annotation which is a convenience annotation and includes @SpringBootApplication, @EnableDiscoveryClient and @EnableCircuitBreaker annotations. For enabling circuit breaker pattern with Hystrix @EnableCircuitBreaker annotation is required.

@SpringCloudApplication
public class SpringBootUserApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootUserApplication.class, args);
  }
}

Now if all the projects EurekaServer, User and Account are running then accessing the URL http://localhost:8080/1 gives the result.

Hystrix circuit breaker

Now stop the Account Service in order to see how circuit breaker works. Accessing the URL http://localhost:8080/1 returns an empty list now.

Microservice circuit breaker example

Monitoring circuits using Hystrix Dashboard

We have configured circuit breaker, implemented a fallback method which has to be executed if the remote service is down. But the question is how can we monitor if the circuit is open or closed? For that we can use Hystrix Dashboard. In this section we’ll see how to use Hystrix dashboard for monitoring single application.

For Hystrix dashboard we’ll create a new Spring Boot Application with starter selected as Hystrix dashboard which adds the following dependency.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

In application.properties file add a port-

server.port=7900

I named this SpringBoot application SpringBootHystrix so the application class is SpringBootHystrixApplication in that along with @SpringBootApplication add @EnableHystrixDashboard annotation too.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
public class SpringBootHystrixApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootHystrixApplication.class, args);
  }
}

Changes in User Microservice

Each microservice that has @EnableCircuitBreaker annotation applied either directly or through @SpringCloudApplication has a /hystrix.stream endpoint which outputs circuit metrics. In our case it is the User microservice that uses @EnableCircuitBreaker so some changes are required there to expose hystrix.stream as endpoint.

Add actuator as dependency

You need to add actutator dependency in your project.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Changes in application.properties

Add the endpoints in application.properties

management.endpoints.web.exposure.include=hystrix.stream, health, info

Here health and info are also added but for Hystrix dashboard hystrix.stream is must.

With these changes and application started you can access the Hystrix dashboard by using URL- http://localhost:7900/hystrix/

Hystrix dashboard

From this page we can enter the URL of the service whose /hystrix.stream endpoint is exposed. So enter URL- http://localhost:8080/actuator/hystrix.stream and enter some value for Title. Then click Monitor Stream.

Hystrix Dashboard stream

To check for opened circuit you can close the Account Service (Meaning Account service can't be reached) and create a simulator to send request to http://localhost:8080/1 after few milliseconds. Alternatively you can hit enter several times after entering this URL in browser window. Many failed requests will result in opening of the circuit and that change will be reflected in the Dashboard too.

Dashboard circuit open

That's all for the topic Spring Boot Microservices + Hystrix Circuit Breaker. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment