所谓熔断就是在请求的微服务不能顺利返回时的一种处理机制,形象的成为熔断–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服务是停掉了,所以总会有一个请求失败,失败后由熔断来接着处理。
如果你是直接从这一节开始看的,有疑问可以看一下前一节,因为项目是基于前一节的项目修改的。