断路器(Circuit Breaker)的是微服务系统中一个非常重要且关键的概念。为什么说它重要呢?是因为在微服务的系统中,服务之间是相互调用的,如果其中一个服务挂掉了,其他的服务请求还在不断的请求这个服务,如果没有特殊的处理,那么通常要等请求超时请求才能返回,每个请求就是一个线程,而在这期间线程不会被释放,那么很快调用端就会因为线程阻塞而消耗大量资源,一旦调用端资源耗尽,那么就会引起连锁反应。所以,为了保证服务之间尽可能少的相互影响,就提出了断路器的概念。
Hystrix 是 Netflix 开源的延迟和容错库,它提供了断路器的功能。本节我们来看看如何使用 Hystrix 来达到熔断的效果。
首先添加依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
第二步添加 fallback 方法,并给需要熔断的方法加上 @HystrixCommand 注解。
@HystrixCommand(fallbackMethod = "defaultOrder")
@GetMapping("/orders/{id}")
public Order findById(@PathVariable("id") String id) {
return restTemplate.getForObject("http://order-service/orders/" + id, Order.class);
}
public Order defaultOrder(String id, Throwable e) {
log.error("fall back findById", e);
return new Order("0", "", "0");
}
第三步在启动类上添加 @EnableCircuitBreaker 注解。
在添加断路器之前,我们通过 order-client 调用 order-service 服务的 getById 方法大概需要 10+ 毫秒,如果我们停掉 order-service 服务,响应会 1000+ 毫秒才能返回,可见等待请求超时再返回会使线程阻塞很长时间,在有大量请求访问的时候会导致服务端资源耗尽。当我们启用断路器之后,如果 order-service 服务不可用,会直接返回 fallback 方法的返回值。
断路器的状态可以通过 /actuator/health
查看。
management:
endpoint:
health:
show-details: always
在配置文件中加入 actuator 配置后可以查看 hystrix 的信息。
{
"status": "UP",
"details": {
"diskSpace": {...},
"refreshScope": {...},
"discoveryComposite": {...},
"hystrix": {
"status": "CIRCUIT_OPEN"
}
}
}
hystrix 的断路器并不是始终打开的,当 circuitBreaker.requestVolumeThreshold
(调用次数,默认是 20)在 metrics.rollingStats.timeInMilliseconds
(默认 10 秒)定义的时间内故障率超过 circuitBreaker.errorThresholdPercentage
(默认 50)时,则断路器会打开。
多请求几次 order-client 后可以看到断路器的状态变化。
{
"status": "UP",
"details": {
"diskSpace": {...},
"refreshScope": {...},
"discoveryComposite": {...},
"hystrix": {
"status": "CIRCUIT_OPEN",
"details": {
"openCircuitBreakers": ["OrderClient::findById"]
}
}
}
}
如果我们修改 circuitBreaker 的参数
@HystrixCommand(fallbackMethod = "defaultOrder", commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "100"),
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "1")
})
重启之后调用一次 order-client 就可以看到 CIRCUIT_OPEN 的状态了。
Feign 使用 Hystrix
作为 Netflix 的组件 Feign 也整合了 Hystrix 。本节我们来看看如何使用 Feign 的 Hystrix 。
首先加入依赖,创建一个 feign-client ,可参考 Services Communication using Feign ,然后创建一个 fallback 实现。
@Component
public class OrderServiceFallback implements OrderService {
@Override
public Order findById(String id) {
return new Order("0", "", "0");
}
}
然后在 feign-client 中指定 fallback 。
@FeignClient(name = "order-service", fallback = OrderServiceFallback.class)
如果需要捕获异常可以这样写。
@Component
@Slf4j
public class OrderServiceFallbackFactory implements FallbackFactory<OrderClient> {
private static final Order order = new Order("0", "", "0");
@Override
public OrderClient create(Throwable throwable) {
return new OrderClient() {
@Override
public Order findById(String id) {
log.error("fallback findById", throwable);
return order;
}
};
}
}
在 feign-client 中指定 fallbackFactory 。
@FeignClient(name = "order-service", fallbackFactory = OrderServiceFallbackFactory.class)
最后在配置文件中启用 feign hystrix 配置。
feign:
hystrix:
enabled: true
默认是 false 。