Spring Cloud Learning Notes: Hystrix Fault Tolerance Mechanism

Posted by beachcomber on Tue, 17 Sep 2019 12:58:42 +0200

brief introduction

In the micro-service architecture, the dependencies between micro-services are complex, and some services will inevitably fail, resulting in the thread blockage of remote scheduling for service invokers. In high load scenarios, cascade failures may occur if no processing is done, leading to resource exhaustion of service callers and even the whole system runs out. Hystrix is an open source delay and fault-tolerant Library of Netflix, which helps control the interaction between these micro-services by adding delay tolerance and fault-tolerant logic. Hystrix achieves this by isolating access points between services, stopping cascading failures across services, and providing fallback options, all of which enhance the overall resilience of the system.

Project introduction

  1. sc-parent, parent module (see Spring Cloud Learning Notes (1): Eureka Registry)
  2. sc-eureka, Registry (see Spring Cloud Learning Notes (1): Eureka Registry)
  3. sc-provider, provider (see Spring Cloud Learning Notes (1): Eureka Registry)
  4. sc-consumer-hystrix-ribbon, consumers using Hystrix+Ribbon
  5. sc-consumer-hystrix-feign, consumers using Hystrix+Feign

Using Hystrix on Ribbon

1. Create the sub-module project sc-consumer-hystrix-ribbon under the parent module, pom.xml:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.cf</groupId>
    <artifactId>sc-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>sc-consumer-hystrix-ribbon</artifactId>
  
  <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</artifactId>
    </dependency>
  </dependencies>
</project>

2. Create the startup class consumer.ConsumerApplication:

package consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableCircuitBreaker
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    
    //Integrating Ribbon for RestTemplate to provide load balancing capabilities
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3. Create Controller: consumer.controller.ConsumerController that invokes provider services

package consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    
    @HystrixCommand(fallbackMethod="getBookListFallBack")
    @GetMapping("/getBookList")
    public String getBookList(){
        return restTemplate.getForObject("http://sc-provider/book/list", String.class);
    }
    
    public String getBookListFallBack(){
        return "[\"Java Beginning to Abandoning\"]";
    }
}

@ HystrixCommand: Represents a method that calls the getBookList method as a hystrix command.
fallbackMethod: Specifies the method to handle fallback logic. Here is the getBookListFallBack method. When the getBookList method runs out of an exception, it will call the getBookListFallBack method.
Note: The fallback method should have the same signature as the method called as the hystrix command.

4. Create application.yml:

server:
  port: 8083

spring:
  application:
    name: sc-consumer-hystrix-ribbon
    
eureka:
  client:
    registerWithEureka: false
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/    

5. test

Start the registry sc-eureka, provider sc-provider, consumer sc-consumer-hystrix-ribbon in turn, and visit http://localhost:8083/getBook List. The results are as follows:

This is the normal value returned by the provider. Next, close the provider sc-provider and visit http://localhost:8083/getBookList again. The results are as follows:

Because when the provider sc-provider is shut down, the consumer will report an error when visiting the provider again. When Hystrix catches an exception, it will directly call the fallback method, which is the getBookListFallBack method.

Using Hystrix on Feign

1. Create the sub-module project sc-consumer-hystrix-feign, pom.xml under the parent module:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.cf</groupId>
    <artifactId>sc-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>sc-consumer-hystrix-feign</artifactId>
  
  <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-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  </dependencies>
</project>

2. Create the startup class feign.FeignApplication:

package feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

3. Create a Feign declarative interface: feign.inter.BookService

package feign.inter;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;

import feign.fallback.BookFallBack;

@FeignClient(value="sc-provider", fallbackFactory=BookFallBack.class)
public interface BookService {
    
    @GetMapping("/book/list")
    public String getBookList();
}

@ The fallbackFactory attribute in the FeignClient annotation is the specified Feign client interface that defines the fallback factory.

4. Create Controller that invokes provider services:

package feign.controller;

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 feign.inter.BookService;

@RequestMapping("/feign")
@RestController
public class FeignController {
    @Autowired
    private BookService bookService;
    
    @GetMapping("/getBookList")
    public String getBookList(){
        return bookService.getBookList();
    }
}

5. Create application.yml:

server:
  port: 8084

spring:
  application:
    name: sc-consumer-hystrix-feign

eureka:
  client:
    registerWithEureka: false
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/   

feign:
  hystrix:
    enabled: true  #Turn on hystrix support     

6. Create a fallback factory class:

package feign.fallback;
import org.springframework.stereotype.Component;

import feign.hystrix.FallbackFactory;
import feign.inter.BookService;

@Component
public class BookFallBack implements FallbackFactory<BookService>{
    @Override
    public BookService create(Throwable cause) {
        return new BookService() {
            @Override
            public String getBookList() {
                return "[\"Java Beginning to Abandoning\"]";
            }
        };
    }
}

The create method returns a fallback instance, which is the implementation class of the Feign declarative interface BookService. It provides a fallback method corresponding to the BookService. The fallback method in the implementation class will be called when the BookService interface call fails.

7. test:

Start the registry sc-eureka, provider sc-provider, consumer sc-consumer-hystrix-feign in turn, and visit http://localhost:8084/feign/getBook List. The results are as follows:

This is the normal value returned by the provider. Next, close the provider sc-provider and visit http://localhost:8084/feign/getBookList again. The results are as follows:

8. Check the reasons for the fallback

Modify the BookFallBack factory class:

@Component
public class BookFallBack implements FallbackFactory<BookService>{
    @Override
    public BookService create(Throwable cause) {
        return new BookService() {
            @Override
            public String getBookList() {
                //Output the fallback reason to the console
                cause.printStackTrace(System.out);
                return "[\"Java Beginning to Abandoning\"]";
            }
        };
    }
}

Start the registry sc-eureka, consumer sc-consumer-hystrix-feign in turn, and visit http://localhost:8084/feign/getBook List, console output:

com.netflix.hystrix.exception.HystrixTimeoutException
    at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$1.run(AbstractCommand.java:1142)
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:41)
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:37)
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable.run(HystrixContextRunnable.java:57)
    at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$2.tick(AbstractCommand.java:1159)
        ......

Topics: Java Spring Maven Apache