Spring Cloud Comments (five) Hystrix source code analysis

1. @HystrixCommand

First, we start from @HystrixCommand comment

@HystrixCommand(fallbackMethod = "fallBack")
    public String hiService() {
        return restTemplate.getForObject("http://provider/zone?",String.class);
    }

@HystrixCommand creates an annotation HystrixCommand object for the current method, the main role of this class is for a command mode Command package, run the following two methods:

//用于异步请求,返回的是Future对象,包含服务执行结束时会返回的结果对象。 
public Future<R> queue() {
        /*
         * The Future returned by Observable.toBlocking().toFuture() does not implement the
         * interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;
         * thus, to comply with the contract of Future, we must wrap around it.
         */
        final Future<R> delegate = toObservable().toBlocking().toFuture();
    
        final Future<R> f = new Future<R>() {
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                if (delegate.isCancelled()) {
                    return false;
                }
                if (HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
                    interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
        		}

                final boolean res = delegate.cancel(interruptOnFutureCancel.get());

                if (!isExecutionComplete() && interruptOnFutureCancel.get()) {
                    final Thread t = executionThread.get();
                    if (t != null && !t.equals(Thread.currentThread())) {
                        t.interrupt();
                    }
                }

                return res;
			}
            @Override
            public boolean isCancelled() {
                return delegate.isCancelled();
			}
            @Override
            public boolean isDone() {
                return delegate.isDone();
			}
            @Override
            public R get() throws InterruptedException, ExecutionException {
                return delegate.get();
            }
            @Override
            public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                return delegate.get(timeout, unit);
            }
        	
        };

        /* special handling of error states that throw immediately */
        if (f.isDone()) {
            try {
                f.get();
                return f;
            } catch (Exception e) {
                Throwable t = decomposeException(e);
                if (t instanceof HystrixBadRequestException) {
                    return f;
                } else if (t instanceof HystrixRuntimeException) {
                    HystrixRuntimeException hre = (HystrixRuntimeException) t;
                    switch (hre.getFailureType()) {
					case COMMAND_EXCEPTION:
					case TIMEOUT:
						// we don't throw these types from queue() only from queue().get() as they are execution errors
						return f;
					default:
						// these are errors we throw from queue() as they as rejection type errors
						throw hre;
					}
                } else {
                    throw Exceptions.sneakyThrow(t);
                }
            }
        }

        return f;
    }
//用于同步执行,从依赖服务返回单一对象结果;调用的是queue.get
    public R execute() {
        try {
            return queue().get();
        } catch (Exception e) {
            throw Exceptions.sneakyThrow(decomposeException(e));
        }
    }

Remove HystrixCommand, there HystrixObservableCommand, the main achievement of such two methods:

  • the observe () which is a heat observation
  • toObservable () which is a cold observation

In fact, to achieve excute () is the queue (), while the queue, we can see the following:

//使用了冷观察;并且转化成为Future异步对象。
final Future<R> delegate = toObservable().toBlocking().toFuture();

Therefore, we can know, run Hstrix command are two important parts as follows:

  • Event source processing (cold, hot observation mode)
  • With, asynchronous processing (synchronous wait for its return, Future object using asynchronous, asynchronous processing may be other work, and finally obtain the results desired in the future to return)

The following talk about Hystrix command execution, illustrated by the diagram:

[Picture outside the chain dump failure (img-nm6ix7mM-1563257116165) (C: \ Users \ SAMSUNG \ Desktop \ summer \ Notes \ micro Services Architecture \ Snipaste_2019-07-15_16-41-12.jpg)]

2. HystrixCircuitBreaker breaker

In Hystrix, the breaker core concept, the following is its definition:

public interface HystrixCircuitBreaker {

    //每个Hystrix命令请求,在执行之前,都会调用这个方法,判断当前是否可以执行。
    public boolean allowRequest();
	//判断开闭状态
    public boolean isOpen();
	//当断路器处于半开状态,且命令运行成功,则用于关闭断路器
    void markSuccess();
    }
    

A plurality of circuit breakers implemented using inner class:

//都不实现
static class NoOpCircuitBreaker implements HystrixCircuitBreaker {
        @Override
        public boolean allowRequest() {
            return true;
        }
        @Override
        public boolean isOpen() {
            return false;
        }
        @Override
        public void markSuccess() {
        }
    }
  //默认实现
  static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
        private final HystrixCommandProperties properties;
        private final HystrixCommandMetrics metrics;
        /* track whether this circuit is open/closed at any given point in time (default to false==closed) */
        private AtomicBoolean circuitOpen = new AtomicBoolean(false);
        /* when the circuit was marked open or was last allowed to try a 'singleTest' */
        private AtomicLong circuitOpenedOrLastTestedTime = new AtomicLong();
    //还有具体几个方法的实现。
  }

For the circuit breaker, the circuit breaker will be based on the state itself, that the circuit breaker is turned on, to decide whether to perform this HystrixCommand command, or call fallBack downgrade, opening and closing the circuit breaker following metrics, metrics measure objects acquired by the healthCount judge, this time window defaults to 10S:

  • QPS within a preset threshold value, it returns false, default is 20.
  • If the request is the error percentage below the preset threshold, it returns false.

Source as follows:

 @Override
        public boolean isOpen() {
            if (circuitOpen.get()) {
                return true;
            }
            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 {
                // our failure rate is too high, trip the circuit
                if (circuitOpen.compareAndSet(false, true)) {
                    // if the previousValue was false then we want to set the currentTime
                    circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
                    return true;
                } else {
                    return true;
                }
            }
        }

Determine whether to allow:

 @Override
        public boolean allowRequest() {
          //这个地方要注意,我们看到了,强制开启的优先级大于强制关闭。
            if (properties.circuitBreakerForceOpen().get()) {
                return false;
            }
            if (properties.circuitBreakerForceClosed().get()) {
                isOpen();
                return true;
            }
          	//这里通过两个方法的结合使用,实现关闭、开启的切换
            return !isOpen() || allowSingleTest();
        }
//这个方法的功能是尝试允许命令请求;
 public boolean allowSingleTest() {
            long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
            // 1) if the circuit is open
            // 2) and it's been longer than 'sleepWindow' since we opened the circuit
            if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
                // We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try.
                // If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'.
                if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
                  //尝试允许请求,则使得断路器处于半开状态。
                    return true;
                }
            }
            return false;
        }

Allowing the request to implement the above:

  • By allowSingleTest, try to allow access request.
  • By setting the sleep time, after the sleep time in the past, then the circuit breaker is in "semi-open state", this half-open state, but the actual request is to allow command execution, if they fail to perform, or a timeout, then turn the circuit breaker placed in a fully open state; and after sleep time, attempts allowed to continue in a half-open state.

Turn off the circuit breaker to achieve the following:

   public void markSuccess() {
            if (circuitOpen.get()) {
              //关闭断路器
                if (circuitOpen.compareAndSet(true, false)) {
                    //win the thread race to reset metrics
                    //Unsubscribe from the current stream to reset the health counts stream.  This only affects the health counts view,
                    //and all other metric consumers are unaffected by the reset
                  //重置统计信息
                    metrics.resetStream();
                }
            }
        }

 

 

Released eight original articles · won praise 0 · Views 7270

Guess you like

Origin blog.csdn.net/fedorafrog/article/details/104159205