系统的保险丝-断路器
目前微服务架构的优势(低耦合、可重用、业务敏捷、适合云模式)已经得到广泛的认可,与此同时,微服务架构使得系统的整体架构变得十分脆弱,因为一个客户端调用需要多个微服务配合完成。微服务通过网络上的远程调用取代了单体架构中的内存调用,在所有微服务已经启动并正常运行时能很好的工作。但是,当一个或多个微服务可不用或高延迟时,可能触发系统级联故障,甚至导致整个系统雪崩。客户端的重试策略,只能让服务端变的更糟糕,甚至导致服务端加快宕机。
断路器类似每个家庭里安装的空气开关,当家里所有电器的功率超过限定功率时,为了保护家用电器,空气开关会自动打开切断家庭的所有电源。当我们关闭不必要的电器后,可手动关闭空气开关,家里的供电又恢复正常,而且没有电器因为其它大功率的电器而受损。在系统设计领域也一样,断路器模式可以帮助解决多个系统之间的级联故障(大功率电器影响甚至破坏其他电器),断路器模式可以帮助您构建容错和弹性系统,当关键服务不可用或具有高延迟时,系统依然可以正常运行。
1. 错误,只能接受
所有的服务在某个时间点都会失败,这一点我们只能接受。断路器允许系统优雅地处理这些故障。 断路器概念很简单。 它用跟踪故障的监视器包装一个函数。 断路器可以当作一个有限状态机,断路器有3种不同的状态:关闭,打开和半打开:
- Closed(关闭) – 在一切正常的情况下,断路器处于关闭状态,所有请求都会传递到后端服务。当失败次数超过断路器“跳闸”阀值时,断路器由关闭状态切换到打开状态。
- Open(打开) – 断路器直接返回错误(不调用任何函数)。
- Half-Open(半打开) – 经过一个超时周期后,断路器切换到半打开状态,并检查错误是否仍然存在。在半打开状态模式下,如果一个调用失败,断路器重新切换到打开状态;如果成功,断路器切换到关闭状态。
短路器的状态转移图如下:
断路器状态转移.png
1. 1断路器-关闭状态
当断路器处于关闭状态,所有的调用都会到达后端服务,后端服务可以正常的对请求作出响应。
关闭状态下,请求的UML序列图如下:
关闭状态.png
1.2 断路器-打开状态
假如,后端的微服务对请求的处理正在变慢,断路器会收到所有对该微服务的请求都超时。一旦,请求超时的次数超过设置的阀值,就会触发断路器有关闭状态切换到打开状态。在打开状态,所有对该微服务的请求,断路器都会直接返回错误,而不再将请求转发到微服务,这允许微服务从高负载状态慢慢恢复到正常。
打开状态下,请求的UML序列图如下:
打开状态.png
1.3 断路器-半打开状态
断路器使用称为HALF-OPEN状态的监控和反馈机制来了解微服务是否以已经恢复。 这种机制定期对商微服务进行测试,以检查它是否已经恢复。 如果对微服务的调用超时,则断路器保持在打开状态。 如果调用返回成功,则切换到关闭状态。断路器在HALF-OPEN状态期间所有对微服务的外部调用都会收到一个错误。
半打开状态下,请求的UML序列图如下:
半打开.png
2. 实战
2.1 实战场景
有两个服务,为了简化描述称为server和client。server提供REST接口供客户端查询天气信息,client通过RestTemplate调用server的接口,并最终将天气信息呈现给用户。
2.2 SpringBoot + hystrix
- 服务端
服务端提供REST接口查询天气信息,示意代码如下:
@RequestMapping(value = "/api/v1.0/weather", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<WeatherInfo> getWeatherInfo() throws Exception {
WeatherInfo weatherInfo = new WeatherInfo();
weatherInfo.setType("sunshine");
weatherInfo.setHumidity(random.nextDouble());
weatherInfo.setTemperature(random.nextDouble());
return new ResponseEntity<>(weatherInfo, HttpStatus.OK);
}
- 客户端
- 在SpringBoot中使用hystrix,需要引入对应的依赖,maven依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
- 通过注解
@EnableCircuitBreaker
打开断路器功能:
- 通过注解
@SpringBootApplication
@EnableCircuitBreaker
public class CircuitBreakersClientApplication {
public static void main(String[] args) {
SpringApplication.run(CircuitBreakersClientApplication.class, args);
}
}
- 通过注解
@HystrixCommand
指定断路器打开时调用的方法:
- 通过注解
@HystrixCommand(fallbackMethod = "defaultInfo")
public WeatherInfo getWeatherInfo() {
log.info("enter getWeatherInfo method");
ResponseEntity<WeatherInfo> response = template.getForEntity(URI, WeatherInfo.class);
return response.getBody();
}
2.3 SpringBoot + resilience4j
-
服务端
服务端实现和SpringBoot+hystrix一致 -
客户端
public WeatherInfo queryWeatherInfo() {
try {
return circuitBreaker.executeCallable(new Callable<WeatherInfo>() {
@Override
public WeatherInfo call() throws Exception {
ResponseEntity<WeatherInfo> response = template.getForEntity(URI, WeatherInfo.class);
return response.getBody();
}
});
} catch (Exception e) {
log.info("exception = {}", e.toString());
}
return defaultInfo();
}
完整代码可从Github获取。
3 参考文献
[1] resilience4j
[2] SpringCloud