主要记录在本地搭建SpringCloud Demo时,使用Hystrix模拟服务降级和服务熔断的相关知识点和配置。

Hystrix断路器

1.使用场景

在微服务架构中,服务调用的链路会随着业务的复杂边的越来越长,一旦其中某些服务节点挂掉,就会导致整体链路无法继续进行。

服务雪崩:是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。

比如在一个调用连上,服务A调用服务B,服务B调用服务C,如果此时流量突然激增,服务A和服务B能抗住请求,但是服务C挂了,那么请求都阻塞在服务B上,服务B的线程资源会被逐步消耗完,进而导致服务B也不可用,同理,最终服务A也会不可用。

服务熔断则是解决服务雪崩的方案之一。

2.服务降级

啥时服务降级?如果下游服务响应太慢,服务提供者会进行服务降级,如暂时停掉重要性低的服务来释放出服务器的资源,来保证主业务的可用性,增加服务响应速度;上游服务调用者发现下游服务响应速度太慢,可以在本地调用服务降级逻辑,直接返回给用户,给予用户友好提示,避免卡顿。

3.服务熔断

服务熔断是服务降级的一种方式。

当下游服务因为某种原因变得不可用或者响应过慢时,上游服务为了保证自身的可用性,不再继续调用下游服务,而是直接返回,快速释放线程资源。等待下游服务可用后,再逐步恢复调用。

4.Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库。它提供了服务降级、服务熔断、监控等能力。

Hystrix的状态图如下图所示:

断路器共三个状态,开启、关闭和半开。服务正常时处于关闭状态,当服务调用的错误率达到设定的阈值时,会转变为开启状态。此时会定义一个reset timeout时间计时,过了这个时间后转换为半开状态,然后尝试调用之前异常的服务,如果服务调用成功后,就会恢复服务;如果调用失败,则重新计时。

检测到服务节点可以正常响应后,就会自动恢复调用链路。

Hystrix的工作流程图如下(官网地址):

图中蓝色箭头表示调用路径,红色表示响应路径

  1. 创建对象。首先创建HystrixCommand或者HystrixObserableCommand对象
  2. 执行命令。
  3. 查看缓存。如果当前请求开启了请求缓存,并且缓存命中,则将缓存结果以Observable对象的形式返回。
  4. 检查断路器状态。如果为打开,Hystrix直接转到Fallback处理逻辑,即第8步;如果状态为关闭,则检查服务的线程池或信号量状态。
  5. 检查服务的线程池或信号量状态。如果资源已经被占满,也会转到Fallback处理逻辑;如果有空闲的资源,则转到下一步。
  6. 根据第一步创建的对象,采取相应的方法去请求服务。HystrixCommand.run() :返回一个单一的结果,或者抛出异常。HystrixObservableCommand.construct(): 返回一个Observable 对象来发射多个结果,或通过 onError 发送错误通知。
  7. Hystrix会将调用的信息(成功、失败、拒绝超时等)反馈给断路器,断路器会维护一组计数器来统计这些信息。然后断路器根据这些信息来决定断路器的状态。
  8. 如果调用命令执行失败,断路器会进入fallback回退处理,即服务降级。从图中可以很容易看出,断路器打开状态、资源不足、服务调用失败和超时都会进入服务降级处理逻辑。
  9. 执行成功后,将处理结果直接返回或者以Observable的形式返回。

本地搭建相关配置

首先在主函数上添加@EnableHystrix注解

服务的Service层的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ------------------------------ 服务熔断 -----------------------------------
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "65"), // 跳闸失败率阈值
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("id不能为负数哈...");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "-----> 调用成功,流水号:" + serialNumber;
}

public String paymentCircuitBreakerFallback(@PathVariable("id") Integer id) {
return "paymentCircuitBreakerFallback -------> 请稍后再试......";
}

这里通过访问负数id来模拟服务的不可用,当服务调用失败率达到阈值65%时,就会开启服务熔断,进而调用paymentCircuitBreakerFallback方法。

本地的模拟效果如下,不断使用负数id进行调用,使得失败调用的比例上升,从而改变了断路器的状态,然后我们调用整数id,发现仍然进入了Fallback服务降级逻辑,因为此时断路器状态为open。

我们继续调用正确的id,当断路器经过休眠窗口计时,即10秒后,尝试再次调用服务,发现可以正常调用,则修改断路器的状态为close,之后服务能够正常调用。

Hystrix的监控Dashboard

配置比较简单,我们在本地使用9001端口,使用@EnableHystrixDashboard注解,如下:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}

在服务端都需要添加坐标:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

PS:这里在调试时可能检测不到(Unable to connect to Command Metric Stream/404),需要在服务端的主方法中添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
* 只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}

Dashboard的首页如下,需要填写监控的地址:

然后进入监控页面:

可以看到上图的断路器处于Open状态,图中的80%表示最后10秒服务调用的失败率,折线表示请求的变化率,下面一半表示Hystrix的线程池状态。