简介
本文介绍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():用来闭合断路器。
另外还有三个静态类。
- 静态类Factory中维护了一个Hystrix命令与HystrixCircuitBreaker的关系集合:ConcurrentHashMap<String,HystrixCircuitBreaker>cpircuitBreakersByCommand,其中String类型的key通过HystrixCommandKey定义,每一个Hystrix命令需要有一个key来标识,同时一个Hystrix命令也会在该集合中找到它对应的断路器HystrixCircuitBreaker实例。
- 静态类NoOpCircuitBreaker定义了一个什么都不做的断路器实现,它允许所有请求,并且断路器状态始终闭合。
- 静态类HystrixCircuitBreakerlmpl是断路器接口HystrixCircuitBreaker的实现类,在该类中定义了断路器的4个核心对象。
- HystrixCommandPropertiesproperties:断路器对应HystrixCommand实例的属性对象,它的详细内容我们将在后续章节做具体的介绍。
- HystrixCommandMetricsmetrics:用来让HystrixCommand记录各类度量指标的对象。
- AtomicBooleancircuitOpen:断路器是否打开的标志,默认为false。
- AtomicLongcircuitOpenedOrLastTestedTime:断路器打开或是上一次测试的时间戳。
HystrixCircuitBreakerlmpl对HystrixCircuitBreaker接口的各个方法实现如下所示。
isOpen()
判断断路器的打开/关闭状态。
- 如果断路器打开标识为true,则直接返回true,表示断路器处于打开状态。
- 如果断路器打开标识为false,就从度量指标对象metrics中获取HealthCounts统计对象做进一步判断(该对象记录了一个滚动时间窗内的请求信息快照,默认时间窗为10秒)。
- 如果错误百分比在阈值范围内就返回false,表示断路器处于未打开状态。
- 该阈值的配置参数为circuitBreakerErrorThresholdPercentage,默认值为50。
- 如果它的请求总数(QPS)在预设的阈值范围内就返回false,表示断路器处于未打开状态。
- 该阈值的配置参数为circuitBreakerRequestVolumeThreshold,默认值为20。
- 如果上面的两个条件都不满足,则将断路器设置为打开状态(熔断/短路)。
- 如果是从关闭状态切换到打开状态的话,就将当前时间记录到上面提到的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()
作用:判断请求是否被允许。