Spring Cloud cognitive learning: the use of fuse Hystrix

Keywords: Java Spring less JSON

catalog

💡 The previous article introduced a component called Fegin, which is used to call services declaratively. It is mainly used to solve the problem that the previous service calls are closely coupled with restTemplate. Spring Cloud cognitive learning (3): the use of declarative call Feign
💡 This article introduces a new component, hystrix. Hystrix is a fuse, which can be used to solve the problem of service fuse and service degradation sent in microservice call.

Hystrix


*Hystrix is a service fuse, which is used to solve the situation of service fuse and service degradation.

Service fuse and service degradation

  • It is mainly to solve the problems of service fuse and service degradation.
    Service fuse mainly solves service avalanche. Let's introduce the concept of service avalanche.
  • Service avalanche: if there are many services that need to call A service, but A suddenly gets stuck and responds slowly. In the case of high concurrency, A service will hold too many resources, which will lead to the lack of resources of other services, thus affecting the use of other services, or even causing the whole system to crash.
  • Service fuse: what is the concept of fuse? That is to say, if you use super powerful appliances in your home, the power switch in your home will help you to trip and cut off power in order to avoid danger. The same is true for service avalanches, which prevent services from taking up too much resources.
    Principle: the user's request will no longer directly call the service, but through a thread pool to call the service. When there are no idle threads in the thread pool, a simple execution result will be returned (it needs to be set, which may be a prompt or a false data).
  • Service degradation: when the resources of a major server are insufficient, other less important services may need to be shut down to make room for it (as compared with the payment function, the function of changing the avatar can temporarily dedicate itself to the payment function). This is the degradation of the service. Although it has been downgraded, in theory, it should also give consumers a guaranteed response, such as a return prompt that the service has been shut down. Since the service provider is closed at this time, this judgment can only happen to the consumers of the service.


💡 For the above two cases, if the service is broken, it can be deployed to both the consumer and the producer, because it's just a "wait too long, don't wait" process, which can be judged by both the consumer and the producer; if the service is degraded, because most of the service providers are closed at this time, the Hystrix can only On the consumer side.

💡 : because Hystrix is actually dealing with error situations, the excessive consumption of resources (avalanche) and degradation of services are actually unavailability of services, and their processing is the same in Hystrix. So here's a demonstration based on the deployment location of Hystrix.


Simple use example:

The following code can be referred to: Simple use experiment of Hystrix

Deploy to service provider

This time, we modify the module spring-cloud-user-service-8003
1. Import dependency:

        <!--increase hystrix rely on start-->
        <!--Dependencies for older versions:
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--increase hystrix rely on end-->

2. Modify the UserController to add a Hystrix processing method listuserbyhystrix to listUser.
@HystrixCommand(fallbackMethod = "listUserByHystirx") means that when unavailability occurs, listUserByHystirx will be called to return the result.

package com.progor.study.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.progor.study.entity.User;
import com.progor.study.service.UserService;
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;

import java.util.ArrayList;
import java.util.List;

// Because the json data is returned, I don't need to annotate @ ResponseBody. Add a RestController
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Integer id) {
        User user = userService.getUser(id);
        if (user == null) {
            throw new RuntimeException("The ID: " + id + "No corresponding user information");
        }
        return user;
    }

    private static int count = 0;
    @GetMapping("/user/list")
    // Method called when an error occurs in fallbackMethod,
    // commandProperties is used to configure fuses. requestVolumeThreshold represents how many times a request is made, the original method will not be called again, and the error handling method will be called directly.
    @HystrixCommand(fallbackMethod = "listUserByHystirx",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), //Number of requests
    }) //
    public List<User> listUser() throws InterruptedException {
        count = count+1;  // Pretend to have a bug once in a while
        System.out.println(count);
        // When it happens occasionally
//        if (count%2 == 0){
//            Thread.sleep(5000);
//        }
        // All the time:
        Thread.sleep(5000);


        List<User> users = userService.listUser();
        return users;
    }

    public List<User> listUserByHystirx() {
        User user = new User(0,"null","null");
        List<User> users = new ArrayList<>();
        users.add(user);
        return users;
    }

}

<br>

3. Open hystrix in the main program class:

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix // Enable hystrix
public class UserService8003Application {
    public static void main(String[] args) {
        SpringApplication.run(UserService8003Application.class, args);
    }
}

4. Start spring cloud user service-8003 and access http://localhost:8003/user/list. If there is an error, listUserByHystirx will be called to return.
🔵 If you call the 8003 service through the service consumer, it will also call listUserByHystirx to return when an error occurs.
🔵 Note that the code above is in the order of one successful execution and one failed execution.



Deployed to service consumers

At present, we only have one service consumer, so we need to modify spring cloud user consumer-80

1. Import dependency in spring cloud user consumer 80 module:

        <!--increase hystrix rely on start-->
        <!--Dependencies for older versions:
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--increase hystrix rely on end-->

2. Modify MessageController2 and add @ HystrixCommand

// This controller is used to handle the use of fegin
@RestController
public class MessageController2 {

    @Autowired
    private MessageService messageService;

    @GetMapping("/msg2/list")
    // Using HystrixCommand
    @HystrixCommand(fallbackMethod = "listByHystirx",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), //Number of requests
    }) //If the request times reach 5, it is a failure, then call listByHystirx directly
    public Map<String, String> list() {
        return messageService.list();
    }

    public Map<String, String> listByHystirx() {
        Map<String, String> map = new HashMap<>();
        map.put("msg","Server has stopped service");
        return map;
    }
}

3. Modify the main program class and start Hystrix,@EnableHystrix

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "USERSERIVE", configuration = MyConfig.class)
@EnableFeignClients // Using feign
@EnableHystrix
public class UserConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumer80Application.class, args);
    }
}

4. test

  • 💡 Start spring-cloud-user-consumer-80 and spring-cloud-eureka-server-7001
  • 💡 Visit once http://localhost/msg2/list . Since there is no service instance, listByHystirx should be called to return the result.
    • Please note that the MessageService we created before is in 8004 and 8005. If we did not start 8004 or 8005, there should be no MessageService instance in eureka at this time, so you will find that we will call the result in our error handling method to return to us, and if we call http://localhost/msg/list Since we didn't do the hystrix processing, we would report wrong;
  • 💡 Start spring-cloud-message-service-8004, visit http://localhost/msg2/list , was successfully accessed. [but since we set the retry times to 5, if you start 8004 or the access fails, try to restart 80, or you will have to wait for the information of eureka to be pulled again (about 30s). ]
  • 💡 Stop spring cloud message service 8004 and visit http://localhost/msg2/list , will call listByHystirx to return the result.


Integrate feign

🔵 Hystrix can also be integrated with Fegin, which is equivalent to being deployed to service consumers.

The following code can be referred to: Experiment of integrating Feign with Hystrix

1. Modify Feign code

Since we only integrate the fegin in the spring cloud common data module, we will do experiments in spring cloud common data.
1. Import dependency in spring cloud common data module.

        <!--increase hystrix rely on start-->
            <!--Dependencies for older versions:
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-hystrix</artifactId>
            </dependency>-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--increase hystrix rely on end-->

2. Modify MessageService and add fallback in FeignClient annotation

// Because there may be more service consumers for this kind of service, it is put into common dependency.
// Use fallback to specify a class that implements MessageService and calls methods in this class when the service is unavailable
@FeignClient(value = "MESSAGESERIVE",fallback = MessageServiceHystrix.class)
public interface MessageService {
    // Here, use RequestMapping to map the Service provider's methods to the local Service methods
    @RequestMapping(value = "/msg/list", method = RequestMethod.GET)
    Map<String, String> list();
}

3. Create MessageServiceHystrix:

package com.progor.study.service;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

// Note that @ Component should be used
@Component
public class MessageServiceHystrix implements MessageService {
    @Override
    public Map<String, String> list() {
        Map<String, String> map = new HashMap<>();
        map.put("msg","Server has stopped service");
        return map;
    }
}

2. Modify consumers

1. Because we used Hystrix in MessageController2, we created a new MessageController3:

@RestController
public class MessageController3 {

    @Autowired
    private MessageService messageService;

    @GetMapping("/msg3/list")
    public Map<String, String> list() {
        return messageService.list();
    }
}

2. Modify the application.yml Add the following:

feign:
  hystrix:
    enabled: true # Used to open hystrix in fegin

🔴 Note that @ EnableHystrix in the main program class can not


3. Test:

💡 Start spring-cloud-user-consumer-80 and spring-cloud-eureka-server-7001 to access http://localhost/msg2/list

  • 💡 Visit once http://localhost/msg3/list . Since there is no service instance, listByHystirx should be called to return the result.
    • Please note that the MessageService we created before is in 8004 and 8005. If we did not start 8004 or 8005, there should be no MessageService instance in eureka at this time, so you will find that we will call the result in our error handling method to return to us, and if we call http://localhost/msg/list Since we didn't do the hystrix processing, we would report wrong;
  • 💡 Start spring-cloud-message-service-8004, visit http://localhost/msg2/list , was successfully accessed. [if you start 8004 or the access fails, try to restart 80, otherwise you have to wait for the information of eureka to be pulled again (about 30s). ]
  • 💡 Stop spring cloud message service 8004 and visit http://localhost/msg2/list , will call listByHystirx to return the result.

Hystrix Dashboard


💡 The Hystrix Dashboard is a component that monitors the status of Hystrix fuses. It has a graphical data statistics interface. You can judge the service status by viewing the fuse statistics.
💡 The way that restTemplate and fegin integrate the Hystrix Dashboard is the same.

To demonstrate the integration, please refer to the following code: Experiment of integrated use of Hystrix Dashboard PS, the comment of commit here the previous version submitted a wrong code. Please take this version as the standard. I think I submitted a wrong version, but I didn't expect that my revert was successful. Just refer to the code of this version. There are no errors in the previous version of Hystrix integration Feign.


1. Configure and start the Hystrix Dashboard

0. Create module spring cloud hystrix dashboard-9001
1. Import dependency:

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

2. Add the annotation @ EnableHystrixDashboard to the main program class:

@SpringBootApplication
@EnableHystrixDashboard
public class SpringCloudHystrixDashboard9001Application {

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

}

3. Visit http://localhost:9001/hystrix, if you can see the page normally, the startup is successful.
💡 The Hystrix Dashboard service is started above, but the Hystrix Dashboard is a component that receives the data of the service. The service does not open the data, and it also cannot receive the data. The following configuration will be performed.



2. Modify service provider

1. Add dependency in service provider spring cloud user service 8003:

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

2. Add ServletRegistrationBean's Bean to the main program class of spring cloud user service-8003: [this is added in Finchley as if it didn't match the Bean before]

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix // Enable hystrix
public class UserService8003Application {
    public static void main(String[] args) {
        SpringApplication.run(UserService8003Application.class, args);
    }
    @Bean
    public ServletRegistrationBean getServlet(){

        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.addUrlMappings("/hystrix.stream");//route
        return registrationBean;
    }
}

3. Modify 8003 fuse handling code:
Because the Hystrix dashboard can only detect the method that is processed by the fuse. There is no method of fuse handling that can not be monitored. Here, the annotation is untied for the mapping effect of the Hystrix dashboard when the test is good or bad.



3. Test:

💡 Start spring cloud user service-8003, spring cloud Eureka server-7001
💡 View in Google browser localhost:8003/hystrix.stream [if it is in Firefox, it may be downloaded. ], this is the 8003 open request flow, which is actually analyzed by the Hystrix dashboard.

When you visit http://localhost : in 8003 / user / list, because of our code problem, it occasionally has problems. At this time, the ping in the figure above will show different contents.
💡 visit http://localhost:9001/hystrix is a page as follows:

Enter in the middle http://localhost:8003/hystrix.stream , let the Hystrix Dashboard monitor the flow, and then click the following button to enter a figure like this:
Delay is the refresh time of the monitor.



7 colors: for the 7 colors in the middle, please refer to the color problem in the upper right corner.

  • Success: on behalf of successful request
  • Short circuited: represents the number of fuses
  • Bad Request: represents the number of times the HystrixBadRequestException was thrown
  • Timeout: represents the number of times the request has timed out
  • Rejected: number of thread pool rejects
  • Failure: request to throw an exception
  • Error: error rate in the last 10 seconds
    1 circle: this circle will grow with the sum of the seven numbers. The larger the circle, the more visits it will have. The color of the circle is a mixture of the colors. For example, when the success is the main part, it tends to be green. If it tends to be red, it means that the request fails a lot. [green, yellow, orange, red]

    Line 1: the line is the number of requests. If there is no request, it is a straight line. If there is a request, it is a line chart.
    For more information, please refer to: Official reference document of data in the figure


Add:

  • More (including working mechanism, aggregation of multiple hystrix dashboards based on Turbo.) , will be explained in a single chapter, googoogoo.

Posted by amma on Fri, 15 May 2020 00:21:43 -0700