简介

本文介绍SpringCloud的hystrix的断路器的原理。

本内容也是Java后端面试常见的问题。

原理

简述

断路器是如何决策熔断和记录信息的呢?

看断路器HystrixCircuitBreaker的定义:

public interface HystrixCircuitBreaker {
	public static class Factory {...}
	static class HystrixCircuitBreakerlmpl implements HystrixCircuitBreaker {… }
	static class NoOpCircuitBreaker implements HystrixCircuitBreaker {… }
	public boolean allowRequest();
	public boolean isOpen();
	void markSuccess() ;
}

可以看到它的接口定义并不复杂,主要定义了三个断路器的抽象方法。

  • allowRequest():判断是否允许执行Hystrix命令的请求。
  • isOpen():返回当前断路器是否打开。
  • markSuccess():用来闭合断路器。

另外还有三个静态类。

  1. 静态类Factory中维护了一个Hystrix命令与HystrixCircuitBreaker的关系集合:ConcurrentHashMap<String,HystrixCircuitBreaker>cpircuitBreakersByCommand,其中String类型的key通过HystrixCommandKey定义,每一个Hystrix命令需要有一个key来标识,同时一个Hystrix命令也会在该集合中找到它对应的断路器HystrixCircuitBreaker实例。
  2. 静态类NoOpCircuitBreaker定义了一个什么都不做的断路器实现,它允许所有请求,并且断路器状态始终闭合。
  3. 静态类HystrixCircuitBreakerlmpl是断路器接口HystrixCircuitBreaker的实现类,在该类中定义了断路器的4个核心对象。
  1. HystrixCommandPropertiesproperties:断路器对应HystrixCommand实例的属性对象,它的详细内容我们将在后续章节做具体的介绍。 
  2. HystrixCommandMetricsmetrics:用来让HystrixCommand记录各类度量指标的对象。
  3. AtomicBooleancircuitOpen:断路器是否打开的标志,默认为false。
  4. AtomicLongcircuitOpenedOrLastTestedTime:断路器打开或是上一次测试的时间戳。

HystrixCircuitBreakerlmpl对HystrixCircuitBreaker接口的各个方法实现如下所示。

isOpen()

判断断路器的打开/关闭状态。

  1. 如果断路器打开标识为true,则直接返回true,表示断路器处于打开状态。
  2. 如果断路器打开标识为false,就从度量指标对象metrics中获取HealthCounts统计对象做进一步判断(该对象记录了一个滚动时间窗内的请求信息快照,默认时间窗为10秒)。
  1. 如果错误百分比在阈值范围内就返回false,表示断路器处于未打开状态。
  1. 该阈值的配置参数为circuitBreakerErrorThresholdPercentage,默认值为50。
  1. 如果它的请求总数(QPS)在预设的阈值范围内就返回false,表示断路器处于未打开状态。
  1. 该阈值的配置参数为circuitBreakerRequestVolumeThreshold,默认值为20。
  1. 如果上面的两个条件都不满足,则将断路器设置为打开状态(熔断/短路)。
  1. 如果是从关闭状态切换到打开状态的话,就将当前时间记录到上面提到的circuitOpenedOrLastTestedTime对象中。

isOpen()源码 

@Override
public boolean isOpen() {
    if (circuitOpen.get()) {
        // if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to close
        return true;
    }

    // we're closed, so let's see if errors have made us so we should trip the circuit open
    HealthCounts health = metrics.getHealthCounts();

    // check if we are past the statisticalWindowVolumeThreshold
    if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
        // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
        return false;
    }

    if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
        return false;
    } else {
        if (circuitOpen.compareAndSet(false, true)) {
            circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
            return true;
        } else {
            // How could previousValue be true? If another thread was going through this code at the same time a race-condition could have
            // caused another thread to set it to true already even though we were in the process of doing the same
            // In this case, we know the circuit is open, so let the other thread set the currentTime and report back that the circuit is open
            return true;
        }
    }
}

allowRequest()

作用:判断请求是否被允许。