SpringCloud系列-Hystrix 微服务容错保护
SpringCloud Hystrix实现了断路器、线程隔离等一系列服务保护功能。它也是基于Netflix的开源框架Hystrix实现的,该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。
服务调用关系图:
在学习Hystrix之前,我们先来了解一下容错及容错的手段。
前面介绍了Eureka与Ribbon之后,我们了解了服务提供者、服务消费者、注册中心之间的关系,如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃。
雪崩效应
微服务架构的应用系统通常包含多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,微服务之间难免存在依赖关系。现实场景中,我们常把“基础服务故障“导致“级联故障”的现象称为雪崩效应。雪崩效应描述的是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。
如何容错
要想防止雪崩效应,必须有一个强大的容错机制。该容错机制需要实现以下两点。
- 为网络请求设置超时
通常情况下,一次远程调用对应着一个线程/进程。如果响应太慢,这个线程、进程就得不到释放。而线程/进程又对应着系统资源,如果得不到释放的线程/进程月积越多,资源就会被逐渐被耗尽,最终导致服务的不可用。
因此,必须为每个网络请求设置超时,让资源尽快释放。 - 使用断路器模式
如果某个微服务的请求有大量超时,再去让新的请求访问该服务已经没有任何意义,只会无畏消耗资源。
断路器可理解为对容易导致错误的操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。
断路器可以实现快速失败,如果它在一段时间类检测到许多类似的错误,就会在之后的一段时间内,强迫对该服务的调用快速失败,即不在请求所依赖的服务。这样,应用程序就无须在浪费CPU时间去等待长时间的超时。
断路器也可自动诊断依赖的服务是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。使用这种方式,就可以实现微服务的“自我修复“——当依赖的服务不正常是打开断路器时快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。
Hystrix简介
Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提神系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
- 包裹请求:
使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用到了设计模式中的“命令模式”。 - 跳闸机制:
当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务的一段时间。 - 资源隔离
Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。 - 监控:
- Hystrix可近乎实时地监控运行指标和配置的变化,例如成功、失败、超时以及被拒绝的请求等
- 回退机制:
当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。 - 自我修复:
- 断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换
Hypstrix 设计原则:
- 防止单个依赖耗尽所有容器用户线程
- 提供降级服务,以保护用户请求失败
- 使用隔离技术,阻断一个依赖故障的传播
- 近实时的指标、监控
Hystrix 设计原理
- 对外部系统或者依赖的调用都封装在HystrixCommand 或者HystrixObservableCommand
对象中,该对象通常单独的在一个线程中执行 - 可自定义设置服务调用超时时间
- 为每一个依赖调用维护一个小线程池,如果它满了,则调用会被拒绝,而不是排队阻塞
快速开始
准备工作
在之前的章节当中,我们都搭建了以下的模块:
注册中心Eureka
服务提供者server-provider
服务调用者server-consumer
引入Hystrix
我们在服务调用者server-consumer引入hystrix依赖,并且改造原来的服务调用接口:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Fegin的调用接口HelloApi:
@FeignClient(name = "server-provider",configuration = MyFeignConfig.class)
public interface HelloApi {
@RequestMapping("/sayHello")
public String sayHello(@RequestParam("name") String name);
}
新建控制器HystrixController,注入Feign接口,调用服务提供者,添加 @HystrixCommand注解,当服务调用失败时,我们通过fallbackMethod指定失败后调用的本地的方法名称,注意参数必须带上:
@RestController
@RequestMapping("/hystrix")
public class HystrixController {
@Autowired
HelloApi helloApi;
@RequestMapping("/sayHello")
@HystrixCommand(fallbackMethod = "sayError")
public String sayHello(String name) {
return helloApi.sayHello(name);
}
public String sayError(String name){
return "sayError!";
}
服务提供端server-provider改造,让接口有一般的几率调用失败:
@RestController
public class RibbonController {
@Value("${server.port}")
private String port;
@RequestMapping("/sayHello")
public String sayHello(String name) {
Random random = new Random();
if (random.nextInt(10) < 5) {
throw new RuntimeException("erroe");
}
return "from:" + port + ",hello!," + name;
}
接下来我们启动Eureka注册中心,两个服务提供者,一个服务调用者:
使用浏览器调用接口:http://localhost:5168/hystrix/sayHello?name=name,有一半的几率是返回"sayError!":
这样就简单的实现了熔断、降级的功能。
注意:上一章我们整合了Feign和Hystrix,Feign在配置的时候我们可以指定@FeignClient注解中的fallback,从这里我们可以看出来,Feign默认是集成了Hystrix的。如果我们同时配置了Feign的fallback,同时也配置了 @HystrixCommand(fallbackMethod = “sayError”),那么Feign的配置优先。
Hystrix更多配置
我们可以在@HystrixCommand注解中添加属性,自定义配置,通过查看@HystrixCommand注解的源代码,
我们可以配置
- commandProperties: 指令属性
- threadPoolProperties:线程池的属性
等等,例如:
```bash
@RestController
@RequestMapping("/hystrix")
public class HystrixController {
@Autowired
HelloApi helloApi;
@RequestMapping("/sayHello")
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
@HystrixProperty(name = "execution.timeout.enabled", value = "false")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "101"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
},
fallbackMethod = "sayError")
public String sayHello(String name) {
return helloApi.sayHello(name);
}
public String sayError(String name) {
return "sayError!";
}
}
- execution.isolation.thread.timeoutInMilliseconds
:设置thread和semaphore两种策略的超时时间,默认是1000毫秒,这也是我们写程序经常忽略的一点,命名接口没错,但是调用就是失败,这里我们可以设置超时时长。 - execution.timeout.enabled:是否开启超时,默认是true
- execution.isolation.thread.interruptOnTimeout:发生超时时,是否中断线程,默认true
- circuitBreaker.sleepWindowInMilliseconds:熔断后多久开始尝试恢复
- circuitBreaker.errorThresholdPercentage:出错百分比阈值,默认是50%
当然我们也可以配置在yml配置文件中:通过hystrix.command.[commandKey],commandKey 默认是default,对于Zull而言就是 service ID:
hystrix:
command:
default:
execution:
timeout:
enabled : false
当服务端因为网络延迟或故障出险问题时,我们可以通过Hystrix实现服务熔断,那么如何做到迅速的发现问题并且迅速的解决问题呢?熔断的监控工具有2款工具:Hystrix-dashboard和Turbine,Hystrix-dashboard是针对Hystrix进行实时的监控工具,通过Hystrix-dashboard我们可以直观的看到单个应用的服务信息。但是,在分布式的模式下,我们需要一个能够汇总所有的服务数据并直观的显示出来,这个工具就是Turbine。’
Eureka是默认使用心跳机制来监测服务的健康与否的,我们通过在服务端引入actuator依赖,并修改yml配置,开启健康检查,能够查到服务是否正常,这里在需要被监测的服务组件中,开启监控也需要引入actuator:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml 添加配置,默认只有"health",“info”,这里我们添加"hystrix.stream":
management:
endpoints:
web:
exposure:
include: ["health","info","hystrix.stream"]
在被检测的项目中引入actuator就可以通过接口的方式收集Hystrix Command标注的资源接口参数信息,我们还可以通过直观的方式,使用Hystrix-dashboard仪表盘的方式展示:
首先,新建仪表盘应用Monitor,创建Maven项目,引入hystrix-dashboard依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
在主类上添加@EnableHystrixDashboard注解,表示开启仪表盘:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
访问http://localhost:8412/hystrix访问仪表盘的页面:
控制主页面,我们可以输入指定服务的接口,刷新时间,默认是2秒就可以显示指定服务的监控数据了,比如我们可以输入server-consumer服务消费者的url http://localhost:5168/actuator/hystrix.stream ,就可以实时的显示以下的信息:
具体的信息,包括了成功数量、熔断数量,错误数量,错误的百分比等等信息: