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;
    }
相關文章
相關標籤/搜索