Hystrix of SpringCloud: Cluster Fault Tolerance Framework

In a distributed environment, some dependent services may fail, affecting the stable operation of the system.
Hystrix controls the interaction of components between distributed systems by adding delay thresholds and fault-tolerant logic.
Hystrix achieves fault tolerance by isolating access points between services, stopping cascading failures between them, and providing fallback operations.

The following example is for use in Spring Cloud.
Development tools: IntelliJ IDEA 2019.2.3

One, server side

1. Create a project

Create a new SpringBoot project in IDEA, the name is "spring-hystrix-server", SpringBoot version select 2.1.10, in the selection of Dependencies (dependencies) interface, check Spring Cloud Discovery ->
Eureka Server, create the pom after completion. The xml configuration file automatically adds dependencies on the latest stable version of SpringCloud, currently Greenwich.SR3.
The complete content of pom.xml is as follows:

<?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.1.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-hystrix-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-hystrix-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>

</project>

2. Modify the configuration application.yml

Modify the port number to 8761; cancel the registration of your information to the Eureka server, and do not grab the registration information from the Eureka server.

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

3. Modify the startup code

Add annotation @EnableEurekaServer

2. Service provider

1. Create a project

Create a new SpringBoot project in IDEA, except that the name is "spring-hystrix-provider", the other steps are the same as the server-side creation above.

2. Modify the configuration application.yml

server:
  port: 8080
spring:
  application:
    name: spring-hystrix-provider
eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

3. Add an entity class User.java

package com.example.springhystrixprovider;

public class User {
    String name;
    Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

4. Modify the startup code

package com.example.springhystrixprovider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class SpringHystrixProviderApplication {

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

    @RequestMapping("/hello")
    public String hello(HttpServletRequest request) {
        return "hello world." + request.getServerPort();
    }

    @RequestMapping(value="/user/{name}", produces = MediaType.APPLICATION_JSON_VALUE)
    public User user(@PathVariable String name) {
        User u = new User();
        u.setName(name);
        u.setAge(30);
        return u;
    }
}

Three, the service caller

1. Create a project

Create a new SpringBoot project in IDEA, the name is "spring-hystrix-invoker", SpringBoot version select 2.1.10, in the interface of selecting Dependencies (dependencies) check Spring Cloud Discovery -> Eureka Server, Spring Cloud Circuit Breaker -> Hystrix.
The complete content of pom.xml is as follows:

<?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.1.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-hystrix-invoker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-hystrix-invoker</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</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>
        </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>

</project>

2. Modify the configuration application.yml

server:
port: 9000
spring:
application:
name: spring-hystrix-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/

3. Modify the startup code

Added annotations @EnableEurekaClient and @EnableCircuitBreaker.

package com.example.springhystrixinvoker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class SpringHystrixInvokerApplication {

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

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

}

4. Add an entity class User.java

package com.example.springhystrixinvoker;

public class User {
    String name;
    Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

5. Add a service class and call the service in the service method

The service method is decorated with @HystrixCommand annotation, and the fallback method is configured.package com.example.springhystrixinvoker;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

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

@HystrixCommand(fallbackMethod = "getUserFallback")
public User getUser(String name){
User user = restTemplate.getForObject("http://spring-hystrix-provider/user/{name}", User.class, name);
return user;
}

public User getUserFallback(String name){
User user = new User();
user.setName("无名");
user.setAge(20);
return user;
}
}

6. Add the controller InvokerController.java

package com.example.springhystrixinvoker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

    @RequestMapping(value = "/invokeUser/{name}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public User invokeUser(@PathVariable String name){
        User user = userService.getUser(name);
        return user;
    }
}

Four, test

1. Start the server.
Browser visit http://localhost:8761/, normal
2. Start the service provider
Browser visit http://localhost:8080/user/ Xiaoming
page output: {"name":"小明","age":30 }
3. Start the service caller.
Browser visit http://localhost:9000/invokeUser/Xiaoming
page output: {"name":"Xiaoming","age":30}
4. Stop the 8080 service provider, and then visit the 9000 address
page output: {"name ":"Unnamed","age":20}
It can be seen that the fallback method was triggered because the call failed.

Five, cache annotations

Hystrix supports caching. If the same interface is called in multiple places during a request , you can consider using caching.
Caching can be implemented through annotations, and the caching and merge request functions need to be initialized before the request context can be implemented.

1. Create a new javax.servlet.Filter to create and destroy Hystrix request context

package com.example.springhystrixinvoker;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*", filterName = "hystrixFilter")
public class HystrixFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try{
            chain.doFilter(request,response);
        }finally {
            context.shutdown();
        }
    }

    public void destroy() {
    }
}

2. Add the annotation @ServletComponentScan to the startup class

3. Write service methods and use cache annotations

(1) @CacheResult The modified method returns the result will be cached, and @HystrixCommand collocation
(2) @CacheRemove invalidates the cache
(3) @CacheKey modifies the method parameter, which means that the parameter is used as the cache key

package com.example.springhystrixinvoker;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.stereotype.Component;

@Component
public class CacheService {
    @CacheResult
    @HystrixCommand(commandKey = "removeKey")
    public String cacheMethod(String name){
        System.out.println("执行cacheMethod方法");
        return "hello";
    }

    @CacheRemove(commandKey = "removeKey")
    @HystrixCommand(commandKey = "removeKey")
    public String updateMethod(String name){
        System.out.println("执行updateMethod方法");
        return "update";
    }
}

4. Call in the controller

Browser access: http://localhost:9000/cacheMethod, IDEA console output:控制器调用服务:0
执行cacheMethod方法
控制器调用服务:1
控制器调用服务:2
控制器调用服务:3
控制器调用服务:4
控制器调用服务:5
执行updateMethod方法
控制器调用服务:6
执行cacheMethod方法
控制器调用服务:7
控制器调用服务:8
控制器调用服务:9

6. Integration of Feign and Hystrix

Modify the code of the service caller project
1. Add Feign dependency to pom.xml

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

2. Modify the configuration application.yml

Add the following configuration: Open Feign's Hystrix configuration

feign:
  hystrix:
    enabled: true

3. Add annotation @EnableFeignClients to the startup class

4. New Feign interfacepackage com.example.springhystrixinvoker;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

//指定调用的服务名称
@FeignClient(name="spring-hystrix-provider", fallback = UserClient.UserClientFallback.class)
public interface UserClient {
@RequestMapping(method = RequestMethod.GET, value = "/user/{name}")
User getUser(@PathVariable("name") String name);

@Component
static class UserClientFallback implements UserClient{
public User getUser(String name){
User user = new User();
user.setName("无名");
user.setAge(20);
return user;
}
}
}

5. Controller InvokerController, add code

    @Autowired
    private UserClient userClient;

    @RequestMapping(value = "/invokeUser2/{name}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public User invokeUser2(@PathVariable String name){
        User user = userClient.getUser(name);
        return user;
    }

 

Guess you like

Origin blog.csdn.net/gdjlc/article/details/115007862