SpringCloud 使用 Hystrix 实现服务端接口【熔断】
Hystrix 在服务端接口默认开启并支持了熔断机制,可以使用 @HystrixCommand 注解中的 commandProperties 进行熔断参数自定义配置,默认情况下对于一个接口来说,5 秒钟发生 20 次降级,或者失败率达到 50% 时,就会触发熔断机制。
所谓熔断机制,可以举一个简单的例子进行解释:当 A 服务调用 B 服务,B 服务可能会由于访问量过大或其它原因发生崩溃,如果没有熔断机制,很可能会导致 A 服务也崩溃,从而造成整个系统级联崩溃(雪崩)。有了熔断机制之后,B 服务就会在熔断期间直接返回降级结果,减轻自己的访问压力,A 服务就能够得到 B 服务的快速响应,不会进行线程资源积压消耗殆尽而崩溃。当 B 服务的熔断时间结束后,A 服务如果再次请求 B 服务时,B 服务会允许其中一个访问线程访问来验证 B 服务的可用性,如果可用则 A 服务的所有访问线程后续都可以正常调用 B 服务,反之则继续开启新一轮的熔断周期。
结合上面对熔断机制的描述,我们发现熔断有 3 种状态:
- 正常状态:A 服务的每次请求都可以按照预期正常调用 B 服务。
- 熔断状态:A 服务在熔断时间内调用 B 服务,B 服务不执行自身逻辑代码,直接返回降级结果。
- 半熔断状态:熔断时间结束后,A 服务中仅有一个请求被允许尝试调用 B 服务(其它请求则继续熔断),如果请求成功,则 A 服务的所有请求都可以正常请求 B 服务(恢复到正常状态),否则继续开始新一轮的熔断周期。
本篇博客的 demo 在之前的基础上进行了简单的修改,主要是介绍如何修改服务端接口熔断的配置参数,以及测试的结果介绍。在本篇博客的最后会提供源代码的下载。
一、搭建工程
采用 Maven 搭建 springcloud_hystrix3 父工程,下面包含 3 个子工程:
对于 springcloud_hystrix3 父工程 pom 文件内容如下所示:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jobs</groupId>
<artifactId>springcloud_hystrix3</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>eureka_app</module>
<module>provider_app</module>
<module>consumer_app</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<!--spring boot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<!--Spring Cloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
有关【Eureka 注册中心】的搭建过程,这里就省略了,跟之前博客一模一样。
二、服务提供者搭建
对于服务提供者,是本篇博客的重点,因为熔断的配置就是在服务端进行的。
服务提供者的 pom 文件和 application.yml 配置文件,与之前的 demo 相比,没啥变化,这里不再列出。
下面仅列出服务提供者,提供的 2 个接口,供客户端消费者调用进行测试验证。
package com.jobs.provider.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RequestMapping("/provider")
@RestController
public class ProviderController {
//该服务端接口可以正常访问,不会发生异常,不受熔断机制影响
//也就是说:其它接口发生了熔断而无法访问,不会导致该接口也熔断无法访问
@RequestMapping("/test1/{id}")
public Map GetData(@PathVariable("id") int id) {
Map result = new HashMap();
result.put("status", 0);
result.put("msg", "success");
result.put("get_id_value", id);
result.put("version", UUID.randomUUID().toString());
return result;
}
//-----------------------------------
//该方法会发生异常,从而调用 @HystrixCommand 方法所配置的降级方法
@HystrixCommand(fallbackMethod = "GetErrorFallback", commandProperties = {
//想要查找可以设置的 HystrixProperty 值,可以按两下 shift 键,
//输入 HystrixCommandProperties,然后查看 HystrixCommandProperties 类的构造方法
//设置Hystrix的超时时间,默认1秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
//设置熔断监控时间 默认5秒
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
//设置导致熔断的失败次数。默认20次
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
//设置导致熔断的失败率 默认50%
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "80")
})
@RequestMapping("/test2/{id}")
public Map GetError(@PathVariable("id") int id) {
int num = 0;
if (id == 1) {
//执行此处,会发生异常,导致调用降级方法
num = id / 0;
}
Map result = new HashMap();
result.put("status", 0);
result.put("msg", "success");
result.put("data", num);
result.put("version", UUID.randomUUID().toString());
return result;
}
//定义 GetError 的降级方法
//方法的返回值、参数列表、参数类型,要与原方法保持一致
public Map GetErrorFallback(int id) {
Map result = new HashMap();
result.put("status", 1);
result.put("msg", "fallback...");
result.put("method", "GetError");
result.put("get_id_value", id);
result.put("remark", "GetError服务端接口发生异常,导致降级...");
return result;
}
}
从上面的代码可以看出,对于具体的一个接口,我们可以通过以下 3 个属性参数自定义熔断策略:
- circuitBreaker.sleepWindowInMilliseconds 用于设置熔断监控时间 默认5秒
- circuitBreaker.requestVolumeThreshold 用于设置导致熔断的失败次数。默认20次
- circuitBreaker.errorThresholdPercentage 用于设置导致熔断的失败率 默认50%
对于服务提供者提供的接口:
- /provider/test1/{id} 接口,每次调用都会正常运行,不会发生服务端降级
- /provider/test1/{id} 接口,当 id 为 1 时故意代码运行抛出异常,所以在服务端会产生降级,达到熔断条件后就会熔断,熔断期间调用此接口,id 传入非 1 的值,仍然会返回降级结果,直到熔断结束后才会返回正常结果
注意:/provider/test1/{id} 接口发生熔断,只是自己返回降级结果,不会影响 /provider/test1/{id} 接口的调用和返回正常结果
三、客户端消费者搭建
对于客户端消费者,只是用来验证服务提供者接口的熔断效果,与之前的 demo 相比,没啥变化。
这里列出客户端消费者提供的两个接口,用来调用服务提供者的接口,验证熔断效果。
package com.jobs.consumer.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@FeignClient(value = "PROVIDER-APP", fallbackFactory = ProviderAppClientImpl.class)
public interface ProviderAppClient {
@RequestMapping("/provider/test1/{id}")
Map GetServerData(@PathVariable("id") int id);
@RequestMapping("/provider/test2/{id}")
Map GetServerErrorData(@PathVariable("id") int id);
}
package com.jobs.consumer.controller;
import com.jobs.consumer.feignclient.ProviderAppClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RequestMapping("/consumer")
@RestController
public class ConsumerController {
@Autowired
private ProviderAppClient providerAppClient;
//该接口可以正常访问,不会其它接口熔断的影响
@RequestMapping("/test1/{id}")
public Map GetTest1(@PathVariable("id") int id) {
Map result = providerAppClient.GetServerData(id);
return result;
}
//服务端接口会发生异常,在服务端【有降级处理逻辑】,返回服务端接口降级处理结果
//所以客户端这边,就不会发生降级,直接返回服务端降级后的结果
//服务端接口已经配置了熔断策略,10 秒钟发生 10 次异常,则触发熔断机制
//然后再过 10 秒钟,尝试放行一次请求,如果成功,则恢复正常访问,否则继续熔断 10 秒钟
@RequestMapping("/test2/{id}")
public Map GetTest2(@PathVariable("id") int id) {
Map result = providerAppClient.GetServerErrorData(id);
return result;
}
}
客户端提供了 2 个接口,用于验证服务提供者的熔断效果:
- /consumer/test1/{id} 接口,每次都能够正常调用成功,而且不会因为其它接口发生熔断而导致自己无法调用。
- /consumer/test2/{id} 接口,调用服务端接口时,当传入 id 值为 1 时,服务端返回降级结果,此时不停的进行调用,当服务熔断掉件后,此时 id 值传入非 1 的参数值时,也会返回服务端降级结果,直到熔断结束后才会返回正常结果。
OK,到此为止,有关 Hystrix 的服务端接口熔断实现就介绍完毕。你可以下载本篇博客的源代码进行详细查看。最后可以运行 Demo 程序,调用客户端提供的接口,验证服务端降级效果。本篇博客的 Demo 源代码经过详细测试无误。
本篇博客的源代码下载地址:https://files.cnblogs.com/files/blogs/699532/springcloud_hystrix3.zip