服务熔断——Hystrix

服务熔断——Hystrix

在分布式中,因为存在网络延迟或者故障,所以一些服务调用无法及时响应。如果此时服务消费者还在大量地调用这些网络延迟或者故障的服务提供者,那么很快消费者也会为大量的等待,造成积压,最终导致其自身出现服务瘫痪。

为了克服这个问题,SpringCloud引入了Netflix的开源框架Hystrix来处理这些问题。当服务提供者应延迟或者故障时,就会使得服务消费者长期得不到响应,Hystrix就会对这些延迟或者故障的服务进行处理。这如同电路负荷过大,保险丝会烧毁从而保障用电安全一样,于是大家就形象地称之为断路器。

这样,当服务消费者长期得不到服务提供者响应时,就可以进行降级、服务断路、线程和信号隔离、请求缓存或者合并等处理。

Hystrix断路器

在互联网中,可能存在某一个微服务的某个时刻压力变大导致服务缓慢,甚至出现故障,导致服务不能响应。这里假设用户微服务请求中出现压力过大,服务响应速度变缓,进入瘫痪状态,而这时产品微服务响应还是正常响应。

但是如果出现产品微服务大量调用用户微服务,就会出现大量的等待,如果还是持续量请求的积压,导致产品微服务最终不可用。

可见在分布式中,如果一个服务不可用,而其他微服务还大量地调用这个不可用的微服务,也会导致其自身不可用,其自身不可用之后又可能继续蔓延到其他与之相关的微服务上,这样就会使得更多的微服务不可用,最终导致分布式服务瘫痪

为了防止这样的蔓延,微服务提出了断路器的概念。

断路器就如同电路中的保险丝,如果电器耗电大,导致电流过大,那么保险丝就会熔断,从而保证用电的安全。

同样地,在微服务系统之间大量调用可能导致服务消费者自身出现瘫痪的情况下,断路器就会将这些积压的大量请求“熔断”,来保证其自身服务可用,而不会蔓延到其他微服务系统上。通过这样的断路机制可以保持各个微服务持续可用。

使用降级服务

处理限制请求的方式的策略很多,如限流、缓存等。这里主要介绍最为常用的降级服务。

所谓降级服务,就是当请求其他微服务出现超时(timeout)或者发生故障时,就会使用自身服务其他的方法进行响应。下面模拟这样请求超时的场景在SpringCloud中断路器是由NetFlix的Hystrix实现的,它默认监控微服务之间的调用超时时间为2000ms(2s),如果超过这个超时时间,它就会根据你的配置使用其他方法进行响应。

准备工作

下面我们就在用户的服务端开始增加模拟的超时方法,以便用于测试。

1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/timeout")
public String timeout() {
// 生成一个3000之内的随机数
long ms = (long)(3000L*Math.random());
try {
// 程序延迟,有一定的概率超过2000毫秒
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "熔断测试";
}

这个方法的作用便是模拟服务端超时。他会有一定概率有超过两千毫秒的延迟。而一旦发生了这个延迟。服务就会使用降级策略。

然后我们在产品微服务端中加入Hystrix依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

然后在主执行程序中,使用注解@EnableCircuitBreaker开启熔断器。

接下来继续定义Feign接口以便用于测试。

1
2
3
4

// 调用用户微服务的timeout请求
@GetMapping("/timeout")
public String testTimeout();

断路机制

接下来便需要用到这个注解@HystrixCommand。这个@HystrixCommand会在方法上使用,标注这个方法会使用延迟断路机制。默认的延迟时间是两秒,也就是两千毫秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Ribbon断路
@GetMapping("/circuitBreaker1")
@HystrixCommand(fallbackMethod = "error", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") })
public String circuitBreaker1() {
return restTemplate.getForObject("http://USER/timeout", String.class);
}

// Feign断路测试
@GetMapping("/circuitBreaker2")
@HystrixCommand(fallbackMethod = "error")
public String circuitBreaker2() {
return userService.testTimeout();
}

// 降级服务方法
public String error() {
return "超时出错。";
}

超过两千毫秒服务的降级策略就会使用。此刻会使用error方法。作用响应请求,从而避免请求的积压,保证微服务的的高可用性,并且还可以设置超时的时间。

我们还可看看@HystrixCommand还能做些什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public @interface HystrixCommand {

// HystrixCommand 命令所属的组的名称:默认注解方法类的名称
String groupKey() default "";

// HystrixCommand 命令的key值,默认值为注解方法的名称
String commandKey() default "";

// 线程池名称,默认定义为groupKey
String threadPoolKey() default "";
// 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
String fallbackMethod() default "";
// 配置hystrix命令的参数
HystrixProperty[] commandProperties() default {};
// 配置hystrix依赖的线程池的参数
HystrixProperty[] threadPoolProperties() default {};

// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。我们也可以通过此方法定义哪些需要忽略的异常
Class<? extends Throwable>[] ignoreExceptions() default {};

// 定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
HystrixException[] raiseHystrixExceptions() default {};

// 定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
String defaultFallback() default "";
}

启用Hystrix仪表盘

对于Hystrix,SpringCloud还提供了一个仪表盘(Dashboard)进行监控断路的情况,从而让开发者监控可能出现的问题。

这就又需要新建一个工程,再引入一个依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

配置仪表盘

1
2
3
4
5
server:
port: 6001
spring:
application:
name: hystrix

并且还需要在启动类上增加一个注解:@EnableHystrixDashboard

接着可以打开 6001/hystrix 查看。

从Hystrix仪表盘首页可以看,支持3种监控,前两种是基于Turbine的,一种是默认集群,另一种是指定集群,第三种是单点监控。

这里就简单地讨论一下单点监控。这里还有两个框,个是轮询时间,也就是隔多少时间轮询一次;另一个是标题,也就是仪表盘页面的标题是什么。

从单监控的说明可以看出,只需要给 http: //hystrix-app:port/hystrix.stream 格式的URL给仪表盘即可。

上面已经在产品微服务中使用了Hystrix,只是还需要引入SpringBoot的监控才可以,所以在产品微服务中先引入spring-boot-starter-actuator的依赖。但是,这样还不够,因为对于Actuator端点是不暴露的,为了使端点暴露,需要在产微服务的application.properties上添加属性:

1
2
3
4
5
management:
endpoints:
web:
exposure:
include: healtg,info,hystrix.stream

这里的management.endpoints.web.exposure.include代表Actuator监控对外露的端点,在默认情况下,它只暴露health和info端点,这里增加了hystrix.stream端点,这样仪表盘才能读到HTTP协议下的hystrix信息流。

然后,在打开这个窗口:

点击按钮,仪表盘就会进行监控了。

测试

访问:http://localhost:9001/product//circuitBreaker2。

再去观察仪表盘:

至此,己经完成了让仪表盘监控断路机制的任务。当发生断路的时候,监控就会给予具体的统计和分析。