断路器

现在微服务架构当中,服务一般都是越来越多的,那么,如果出了异常,抛出错误,如果没有进行很好的处理,一般都是很难看的,而且,由于一些错误的产生可能条件很不确定,那么,要优雅的处理这些错误一定是很麻烦的。所以,现在出现了断路器的概念。

什么是断路器?

如同电路断路器,保险丝一样的东西,如果出现用电功率突然增加,那么保险丝就会断了,用来保护电路,确定没问题了之后,更换保险丝,电路重新恢复正常。就如同我们的某段代码出现异常,那么断路器就会执行,就不会对错误的代码进行再次调用了。软件方面的断路器,严格来说就是一个后备程序。

软件断路器

被保护的程序在给定的阀值内出现了异常,那么就可以调用一个后备方法,断路器就处于打开的状态了,以后请求就都会走这个后备方法。如果是暂时性的问题,比如网络问题,过一段时间,网络恢复了,断路器就会偶尔再去请求之前的错误方法,这个时候的状态被称为半开状态,如果方法没问题了,那么,断路器就会关闭,程序正常执行,如果依然失败,就还是开启状态。

断路器的本质

通过代码的层面来看,断路器就如同一个大型的try…catch…,try里面执行代码,catch执行后备操作,但是,断路器是更加智能的,频繁失败的方法,它就会跳过程序,执行进行后备操作。

断路器的应用

一般情况下,断路器都会用在以下场景:

  • 远程请求:RestTemplate为代表的远程请求,请求失败或者耗时很长却并不能一定获取到正确的返回结果
  • 数据库查询:作为暂时性处理,作为标记后续需要处理的慢查询问题,不能依赖断路器
  • 可能比较慢的方法:不一定失败,但是耗时长的任务

微服务中,某个执行缓慢的微服务不能拖慢整个微服务的性能,避免上游的服务产生联级延迟是要避免的。

Hystrix

Netflix Hystrix是断路器模式的Java实现。Hystrix断路器实现了一个切面,会在目标发生失败的时候执行后备方法。为了实现断路器模式,这个切面还会跟踪目标方法的失败频率,如果他超过了设定的某个阀值,就会触发断路的功能,执行后备方法,反之,就会关闭断路,让方法正常执行。

使用Hystrix

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

启用注解

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableHystrix
public class SpringWebApplication {

public static void main(String[] args) {
SpringApplication.run(SpringWebApplication.class, args);
}
}

在对应的方法上使用@HystrixCommand注解,并定义对应的后备方法,如果在使用该方法时出现异常,那么就会去调用后备方法

1
2
3
4
5
6
7
8

@HystrixCommand(fallbackMethod = "getDefaultOrderById")
@GetMapping("/order/{id}")
public Order getOrderById(@PathVariable("id") Long id);

// 后备方法
@GetMapping("/order/{id}")
public Order getDefaultOrderById(@PathVariable("id") Long id);

实际上,我们可以堆积任意数量的后备方法,但是,还是需要提供一个一定成功的方法,这个方法不需要使用断路器

HystrixCommand相关配置

注解@HystrixCommand可以通过的它的属性commandProperties和注解@HystrixProperty来配置相关的使用规则
缓解延时:Hystrix默认是1s的延时,请求超过这个时间,那么就会执行备用方法,可以通过配置来修改

1
2
3
4
5
@HystrixCommand(fallbackMethod = "getDefaultOrderById",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMillisecond",value = "5000")
})
public Order getOrders();

阀值:默认情况下,会保护方法执行20次,而且50%以上的调用在10s之上时,断路器就会进入打开,后续调用就会执行后备方法,之后5s就会进入半打开状态,再次尝试原来的方法。
我们可以调整以上的相关属性,配置如下:

  • circuitBreaker.requestVolumeThreshold:在给定时间内,方法被调用的次数
  • circuitBreaker.errorThresholdPercentage:在给定时间内,方法调用产生失败的百分比
  • metrics.rollingStats.timeoutInMillisecond:控制请求量和错误百分比的滚动时间周期,也就是设置给定时间
  • circuitBreaker.WindowInMillisecond:开大状态下,多久进入半打开状态。半打开状态下,多久去主动尝试原始方法
    1
    2
    3
    4
    5
    6
    7
    8
    // 例如:20秒内调用超过30次而且失败率达到25%的方法进入打开状态
    @HystrixCommand(fallbackMethod = "getDefaultOrderById",
    commandProperties = {
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "30"),
    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "25"),
    @HystrixProperty(name = "metrics.rollingStats.timeoutInMillisecond",value = "20000"),
    })
    public Order getOrders();

Hystrix流

当调用保护方法时,就会收集一些信息,并将其发送到一个HTTP流中,这些数据可以监控正在运行中的应用的健康状态。Hystrix六中包含如下数据:

  • 方法被调用多少次
  • 调用成功了多少次
  • 后备方法调用了多少次
  • 方法超时了多少次

Hystrix流是由Actuator提供的,如果使用就需要引入相关依赖,SpringBoot也对其进行了支持,依赖如下:

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

收集到了流,那么就需要来展示它,同时,Netflix也提供了Hystrix的Dashboard,我们只需要引入相关依赖就可以

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-dashboard</artifactId>
</dependency>

同时,需要加入开启Dashboard的注解@EnableHystrixDashboard

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableHystrix
@EnableHystrixDashboard
public class SpringWebApplication {

public static void main(String[] args) {
SpringApplication.run(SpringWebApplication.class, args);
}
}

加号相关依赖和配置后,就可以访问Dashboard:http://localhost:7879/hystrix 来查看。

hystrix的线程模型

多个执行很慢的请求,hystrix都会进行阻塞,如果它们来自同一个线程池,线程池耗尽了,那么其他的请求就进不来了,所以,为了避免这种情况,hystrix自己维护了一个线程池,来处理这些请求。
hystrix为每项依赖都分配了一个线程池,当hystrix来调用时,就由自己去线程池中的某个线程执行,与调用线程就行隔离,执行时间长的话,就能够允许调用线程不会一直等待,线程耗尽也只会在hystrix托管的线程池中发送。

聚合多个Hystrix流

一个dashboard一次只能监控一个hystrix流,一般情况下,肯定会有多个服务,多个流,那么我们就需要再引入Netflix的Turbine项目,来组合多个流,把它们放在同一个dashboard中。
添加依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

注解:@EnableTurbine

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableHystrix
@EnableHystrixDashboard
public class SpringWebApplication {

public static void main(String[] args) {
SpringApplication.run(SpringWebApplication.class, args);
}
}

如果不想所有的服务都展示,可以通过配置,来选择展示哪些应用

1
2
3
turbine:
app-config: spring-web,spring-message # 应用名,多个应用使用,隔开
cluster-name-expression: "default" # 需要收集的集群流,不设置则不会收集任何信息