hystrix - Fallback是怎么调用的提到了allowRequest办法,咱们看看这里是做了哪些。
public boolean allowRequest() { // 配置了强制熔断,间接返回false if (properties.circuitBreakerForceOpen().get()) { // properties have asked us to force the circuit open so we will allow NO requests return false; } // 强制敞开 if (properties.circuitBreakerForceClosed().get()) { // we still want to allow isOpen() to perform it's calculations so we simulate normal behavior isOpen(); // properties have asked us to ignore errors so we will ignore the results of isOpen and just allow all traffic through return true; } return !isOpen() || allowSingleTest();}public boolean isOpen() { // 获取熔断器的状态,true就是开 if (circuitOpen.get()) { // 如果是开的,还是返回ture,留给半开的时候用 return true; } // 如果是关的,获取HealthCounts信息 HealthCounts health = metrics.getHealthCounts(); // 以后的申请数如果小于设置的阈值,就间接返回false,阐明没开 // 比方咱们设置了20,此时才19,也是没有开的 // 这个值是样品数量的值,只有足够数量的样品,才比拟精确 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; } // 谬误比例小于设定的值,比方谬误的比例超过50%,就开启熔断 if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) { return false; } else { // 这边设置工夫,用于半开状态 if (circuitOpen.compareAndSet(false, true)) { // if the previousValue was false then we want to set the currentTime 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; } }}
流程图如下:
半开状态
下面还有一个办法,allowSingleTest。这个是用于解决半开的时候。
咱们下面晓得,当熔断的时候,会保留一个值,circuitOpenedOrLastTestedTime。这个值加上设置的休眠工夫和以后工夫做比拟,看看能不能允许尝试拜访一次。
public boolean allowSingleTest() { long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get(); // 熔断器开的时候,并且以后工夫>熔断工夫+休眠工夫 // 也就是说休眠工夫设置为5,那5秒后给他一个机会看看能不能拜访,也就是半开状态 // 而后从新设置circuitOpenedOrLastTestedTime if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) { if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) { return true; } } return false;}
HystrixCircuitBreaker创立
allowRequest是HystrixCircuitBreaker的办法,咱们略微回顾一下command的创立,AbstractCommand中,会调用initCircuitBreaker办法。他会通过HystrixCircuitBreaker的Factory来创立的
private static HystrixCircuitBreaker initCircuitBreaker(boolean enabled, HystrixCircuitBreaker fromConstructor, HystrixCommandGroupKey groupKey, HystrixCommandKey commandKey, HystrixCommandProperties properties, HystrixCommandMetrics metrics) { if (enabled) { if (fromConstructor == null) { // get the default implementation of HystrixCircuitBreaker return HystrixCircuitBreaker.Factory.getInstance(commandKey, groupKey, properties, metrics); } else { return fromConstructor; } } else { return new NoOpCircuitBreaker(); }}
在工厂中,他其实会通过缓存来获取的,也就是说每个CommandKey都有对应的HystrixCircuitBreaker。如果缓存没有,则创立一个放入缓存。
public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) { // 从circuitBreakersByCommand缓存里取,key是CommandKey HystrixCircuitBreaker previouslyCached = circuitBreakersByCommand.get(key.name()); if (previouslyCached != null) { return previouslyCached; } // 缓存没有,创立一个放入缓存中 HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics)); if (cbForCommand == null) { // this means the putIfAbsent step just created a new one so let's retrieve and return it return circuitBreakersByCommand.get(key.name()); } else { // this means a race occurred and while attempting to 'put' another one got there before // and we instead retrieved it and will now return it return cbForCommand; }}