SpringCloud之Hystrix:集群容错框架

分布式环境中,可能会有一些被依赖的服务会失效,影响系统的稳定运行。
Hystrix经过添加延迟阈值以及容错的逻辑,以控制分布式系统间组件的交互。
Hystrix经过隔离服务间的访问点、中止它们之间的级联故障、提供可回退操做来实现容错。java

下面例子为在Spring Cloud的使用。
开发工具:IntelliJ IDEA 2019.2.3web

1、服务器端spring

一、建立项目apache

IDEA中建立一个新的SpringBoot项目,名称为“spring-hystrix-server”,SpringBoot版本选择2.1.10,在选择Dependencies(依赖)的界面勾选Spring Cloud Discovert ->
Eureka Server,建立完成后的pom.xml配置文件自动添加SpringCloud最新稳定版本依赖,当前为Greenwich.SR3。
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.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>
View Code

二、修改配置application.yml缓存

修改端口号为8761;取消将本身信息注册到Eureka服务器,不从Eureka服务器抓取注册信息。服务器

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

三、修改启动类代码app

增长注解@EnableEurekaServermaven

 

2、服务提供者分布式

一、建立项目

IDEA中建立一个新的SpringBoot项目,除了名称为“spring-hystrix-provider”,其它步骤和上面建立服务器端同样。

二、修改配置application.yml

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

三、添加一个实体类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;
    }
}

四、修改启动类代码

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;
    }
}

 

3、服务调用者

一、建立项目

IDEA中建立一个新的SpringBoot项目,名称为“spring-hystrix-invoker”,SpringBoot版本选择2.1.10,在选择Dependencies(依赖)的界面勾选Spring Cloud Discovert -> Eureka Server,Spring Cloud Circuit Breaker -> Hystrix。
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.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>
View Code

二、修改配置application.yml

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

三、修改启动类代码

增长注解@EnableEurekaClient和@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);
    }

}

四、添加一个实体类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;
    }
}

五、添加一个服务类,在服务方法中调用服务

服务方法用@HystrixCommand注解进行修饰,并配置了回退方法。

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;
    }
}

六、添加控制器 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;
    }
}

 

4、测试

一、启动服务器端。
浏览器访问http://localhost:8761/,正常
二、启动服务提供者
浏览器访问 http://localhost:8080/user/小明
页面输出:{"name":"小明","age":30}
三、启动服务调用者。
浏览器访问http://localhost:9000/invokeUser/小明
页面输出:{"name":"小明","age":30}
四、中止8080服务提供者,再访问9000地址
页面输出:{"name":"无名","age":20}
因而可知,因为调用失败,触发了回退方法。

 

5、缓存注解

Hystrix支持缓存功能,若是在一次请求过程中,多个地方调用同一个接口,能够考虑使用缓存。
缓存能够经过注解实现,缓存与合并请求功能须要先初始化请求上下文才能实现。

一、新建一个javax.servlet.Filter,用于建立与销毁Hystrix的请求上下文

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() {
    }
}

二、在启动类中加入注解@ServletComponentScan

三、编写服务方法,使用缓存注解

(1)@CacheResult 被修饰的方法返回结果会被缓存,和@HystrixCommand搭配
(2)@CacheRemove 让缓存失效
(3)@CacheKey 修饰方法参数,表示该参数做为缓存的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";
    }
}

四、控制器中调用

浏览器访问:http://localhost:9000/cacheMethod,IDEA控制台输出:

控制器调用服务:0
执行cacheMethod方法
控制器调用服务:1
控制器调用服务:2
控制器调用服务:3
控制器调用服务:4
控制器调用服务:5
执行updateMethod方法
控制器调用服务:6
执行cacheMethod方法
控制器调用服务:7
控制器调用服务:8
控制器调用服务:9

 

6、Feign与Hystrix整合

修改服务调用者项目的代码
一、pom.xml加入Feign依赖

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

二、修改配置application.yml

增长下面配置:打开Feign的Hystrix的配置

feign:
  hystrix:
    enabled: true

三、启动类加上注解@EnableFeignClients

四、新建Feign接口

package 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;
        }
    }
}

五、控制器InvokerController,添加代码

    @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;
    }
相关文章
相关标签/搜索