Fork me on GitHub

熔断器-Ribbon使用Hystrix

目录

本节详细讲解使用Hystrix的通用方式。

简介

Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

  • 包裹请求

使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用到了设计模式中的“命令模式”。

  • 跳闸机制

当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。

  • 资源隔离

Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。

  • 监控

Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。

  • 回退机制

当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。

  • 自我修复

断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换,前面我们已经详细探讨过了,不再赘述。

通用方式使用Hystrix

服务降级

  • 加依赖:
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  • 加注解:在启动类上添加@EnableCircuitBreaker 注解。
  • 使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RequestMapping("/ribbon")
@RestController
@Slf4j
public class RibbonController {
@Autowired
private RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "findByIdFallback")
@GetMapping("/user/{id}")
public User findById(@PathVariable Integer id) {
// 这里用到了RestTemplate的占位符能力
User user = this.restTemplate.getForObject("http://microservice-provider/user/{id}", User.class, id);
// ...电影微服务的业务...
return user;
}

public User findByIdFallback(Integer id, Throwable throwable) {
log.error("进入回退方法", throwable);
return new User(id, "默认用户", "aaaaaa", "13906123017");
}
}
  • 由代码可知,只需使用@HystrixCommand 注解,就可保护该API。这里的”保护“,其实带有三层含义——”超时机制“、”仓壁模式“、”断路器“!如果您不了解这些是什么,或者想要探寻其中原理,可详见:微服务容错

PS

  • 本例使用了fallbackMethod 属性,指定了一个降级方法,如不指定,Hystrix会有一个默认的降级方案,那就是抛异常。
  • 如何知道断路器打开还是关闭呢?还记得健康检查吗?只需访问应用的/actuator/health 端点,即可查看!断路器的状态——当然,你必须添加如下配置:
  • 1
    2
    3
    4
    management:
    endpoint:
    health:
    show-details: always
1
2
3
4
management:
endpoint:
health:
show-details: always
  • 测试

  • 启动microservice-discovery-eureka

  • 启动microservice-provider

  • 启动microservice-consumer-ribbon-with-hystrix

  • 访问http://localhost:8010/ribbon/user/1 ,能正常返回结果

  • 关闭microservice-provider ,再次访问http://localhost:8010/ribbon/user/1 ,可返回类似如下结果,说明当服务提供者时,服务消费者进入了回退方法

1
2
3
4
5
6
{
"userId": 1,
"userName": "默认用户",
"password": "aaaaaa",
"phone": "13908888888"
}
  • 访问http://localhost:8010/actuator/health ,可获得类似如下结果:

1
2
3
4
5
6
7
8
9
10
11
{
"status": "UP",
"details": {
"diskSpace": ...,
"refreshScope": ...,
"discoveryComposite": ...,
"hystrix": {
"status": "UP"
}
}
}
  • 由结果不难发现,此时断路器并未打开!这是为什么呢? 原因是:此时只请求了一次,没有达到Hystrix的阈值——Hystrix设计来保护高并发应用的,它要求10秒(可用hystrix.command.default.metrics.rollingStats.timeInMilliseconds 自定义)以内API错误次数超过20次(用circuitBreaker.requestVolumeThreshold 自定义),此时才可能触发断路器。

  • 持续不断地访问http://localhost:8010/ribbon/user/1 多次(至少20次)

  • 再次访问http://localhost:8010/actuator/health ,可获得类似如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"status": "UP",
"details": {
"diskSpace": ...,
"refreshScope": ...,
"discoveryComposite": ...,
"hystrix": {
"status": "CIRCUIT_OPEN",
"details": {
"openCircuitBreakers": ["MovieController::findById"]
}
}
}
}
  • 由结果可知,此时断路器已经打开,并且列出了是哪个API的断路器被打开了。

获得造成fallback的原因

在实际项目中,很可能需要获得造成fallback的原因,此时可将代码修改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@HystrixCommand(fallbackMethod = "findByIdFallback")
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
// 这里用到了RestTemplate的占位符能力
User user = this.restTemplate.getForObject(
"http://microservice-provider/user/{id}",
User.class,
id
);
// ...电影微服务的业务...
return user;
}
public User findByIdFallback(Long id, Throwable throwable) {
log.error("进入回退方法", throwable);
return new User(id, "默认用户", "aaaaaa", "13906412317");
}

配套代码

microservice-consumer-ribbon-with-hystrix

相关文章