Spring Cloud 快速入门系列之Hystrix–熔断

所谓熔断就是在请求的微服务不能顺利返回时的一种处理机制,形象的成为熔断–Hystrix。

比如我们有一个业务请求,有一个比较长的请求链,例如:

A服务–>B服务–>C服务–>D服务–>E服务

如果此时,D服务所在的服务器出问题,突然掉线,那么此时的D服务之前的服务,就都阻塞,等待其调用的服务响应,如下:

A服务(等待B响应)–>B服务(等待C响应)–>C服务(等待D响应)–>D服务(宕机)–>E服务

而客户端用户,因为请求没有响应,会频繁重新请求,加上原本的请求,A服务收到的请求突增,且请求不能及时处理返回,最终导致ABC服务因为请求过多而耗尽服务器资源,最终也宕机了。

以上就是一个常见的雪崩效应场景。

Spring Cloud 的Hystrix就是为了避免此种情况的发生,请求服务在规定时间没有响应的时候,进入熔断逻辑(降级),快速返回响应给用户,防止当前服务长时间阻塞。

实操开始O(∩_∩)O哈哈~

本节基于前一节ribbon的两个服务service-c和service-d继续演示

1、service-d的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhimei</groupId>
    <artifactId>service-d</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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-ribbon</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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </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>

这里相比上一节service-d的服务,多引入了一个依赖

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

这就是hystrix的依赖包

2、启动类

package com.itzhimei.d;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
@EnableHystrix
public class ServiceDApplication {

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

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

}

3、请求controller改造一下

package com.itzhimei.d.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ServiceDController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDController.class);

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "getRibbonInfoHystrix")
    @RequestMapping(value = "/getRibbonInfo", method = RequestMethod.GET)
    public String getRibbonInfo() {
        String url = "http://SERVICE-C/getRibbonInfo";
        String body = restTemplate.getForEntity(url, String.class).getBody();
        return body;
    }
    public String getRibbonInfoHystrix() {
        LOGGER.info("getRibbonInfo请求失败,熔断方法接入处理:getRibbonInfoHystrix");
        return "这里返回的是熔断处理getRibbonInfoHystrix";
    }
}

相比上一节,getRibbonInfo()方法多了一个注解,@HystrixCommand(fallbackMethod = “getRibbonInfoHystrix”)就是指定了当前请求如果失败,则会调用对应的后备方法,我们专业一点叫降级处理方法。

4、验证效果

停用一个Service-C服务,只留下一个Service-C服务,来模拟请求失败的场景。


发起6次请求,页面显示分别是:

  • 当前请求服务为Service-C
  • 这里返回的是熔断处理getRibbonInfoHystrix
  • 当前请求服务为Service-C
  • 这里返回的是熔断处理getRibbonInfoHystrix
  • 当前请求服务为Service-C
  • 这里返回的是熔断处理getRibbonInfoHystrix

从打印结果来看,首先ribbon是轮询两个服务,因为其中一个Service-C服务是停掉了,所以总会有一个请求失败,失败后由熔断来接着处理。

如果你是直接从这一节开始看的,有疑问可以看一下前一节,因为项目是基于前一节的项目修改的。