02深度解析Spring Cloud Hystrix---hystrixCommandAspect詳解

總結

主要分析了 信號量和線程池 隔離機制

一、HystrixCommandAspect類

我們首先看一下 主要的方法methodsAnnotatedWithHystrixCommand ,其所有代碼如下,可以知道這個方法是對 @HystrixCommand 和 @HystrixCollapser 進行攔截,接下來我們先把其他的一些輔助點先介紹

@Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
    public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
       xxx .... xxxx
    }

1.1 @HystrixCommand註解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
    //組 的名稱 默認是 註解的方法所在類 的類名
    String groupKey() default "";
    
     //命令名稱   默認就是 @HystrixCommand 所在的方法名
    String commandKey() default "";
    
     // 線程池名稱,主要是來表示  HystrixThreadPool 監控,收集等
    String threadPoolKey() default "";

     /**回調方法,就是 失敗或者超時時的方法
     這個方法必須和 @HystrixCommand 註釋的方法在同一個類裏面
      並且有一樣的入參和出參
     */
    String fallbackMethod() default "";

     // 配置相關的參數 ,以Key-Value 的形式,可配置多個,是數組形式
    HystrixProperty[] commandProperties() default {};

     //線程池的屬性配置,也是key-value形式,可配置多個
    HystrixProperty[] threadPoolProperties() default {};

     // 定義哪些異常可以忽略
    Class<? extends Throwable>[] ignoreExceptions() default {};

     /**
      *定義 用哪種模式 運行hystrix observable command.
       * eager - {@link HystrixObservable#observe()},
       * lazy -  {@link HystrixObservable#toObservable()}.
      */
    ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

    HystrixException[] raiseHystrixExceptions() default {};
    
     /*
      默認的回退方法,如果fallbackMethod 和defaultFallback 同時指定了,fallbackMethod優先
      默認的回退方法 不能有入參,出參需要保持一致
     */
    String defaultFallback() default "";
}

1.2 @HystrixCollapser 註解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {

     //合併名稱,默認是 註解的方法名稱
    String collapserKey() default "";

     /* 合併的方法名稱
       合併的方法必須 要是這種特徵:
       java.util.List method(java.util.List)
       合併的方法只能有一個 參數
     */
    String batchMethod();

     /**
     範圍, 默認是一次請求,可以改成 GLOBAL 全局的
     */
    Scope scope() default Scope.REQUEST;

    /**
     * 合併的相關屬性配置
     * 以 key-value 形式配置,可以配置多個,數組
     */
    HystrixProperty[] collapserProperties() default {};
}

1.3 MetaHolderFactory

我們從 HystrixCommandAspect 類裏面有一個 META_HOLDER_FACTORY_MAP屬性,裏面存儲的 是 不同的類型對應的 MetaHolder的Factory

@Aspect
public class HystrixCommandAspect {

    private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;

    static {
        META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
                .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory())
                .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory())
                .build();
    }
}

CommandMetaHolderFactory 和 CollapserMetaHolderFactory 都是繼承了 抽象類MetaHolderFactory,作用就是 根據不同的類型創建一個MetaHolder ,MetaHolder 裏面包含了當前方法 所需的各種必要信息 ,主要分析一下各自的 create 方法即可
在這裏插入圖片描述
CommandMetaHolderFactory 的create 方法邏輯如下

  1. 獲取@HystrixCommand註解的屬性
  2. 根據方法的返回類型判斷 是 同步還是異步 ,這裏有三種方式 同步(SYNCHRONOUS),異步(ASYNCHRONOUS),響應式(OBSERVABLE) 如果是 Future.class返回類型的爲異步
    如果是 (Observable.class, Single.class, Completable.class) 這三種類型中一種,爲OBSERVABLE ,剩下的類型爲 同步方式
  3. 構建 MetaHolder ,將各種必須的參數 填入 MetaHolder 對象裏面,同時獲取 指定的 FallbackMethod 並進行填充,並對 FallbackMethod 進行校驗,比如返回類型的校驗
  4. 獲取@DefaultProperties 註解裏面的配置對原先的進行覆蓋,這個優先級比@HystrixCommand高
    private static class CommandMetaHolderFactory extends MetaHolderFactory {
        @Override
        public MetaHolder create(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
            HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
            ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
            MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
            if (isCompileWeaving()) {
                builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
            }
            return builder.defaultCommandKey(method.getName())
                            .hystrixCommand(hystrixCommand)
                            .observableExecutionMode(hystrixCommand.observableExecutionMode())
                            .executionType(executionType)
                            .observable(ExecutionType.OBSERVABLE == executionType)
                            .build();
        }
    }

1.4 methodsAnnotatedWithHystrixCommand方法

 @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
    public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = getMethodFromTarget(joinPoint);
        Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
        if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
            throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " +
                    "annotations at the same time");
        }
        MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
        MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
        HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
        ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
                metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();

        Object result;
        try {
            if (!metaHolder.isObservable()) {
                result = CommandExecutor.execute(invokable, executionType, metaHolder);
            } else {
                result = executeObservable(invokable, executionType, metaHolder);
            }
        } catch (HystrixBadRequestException e) {
            throw e.getCause() != null ? e.getCause() : e;
        } catch (HystrixRuntimeException e) {
            throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
        }
        return result;
    }

主要流程如下:

  1. 獲取到對應的方法Method
  2. 如果方法的註解上面同時有 @HystrixCommand 和@HystrixCollapser 註解,拋出異常
  3. 根據不同的註解,選擇對應的metaHolderFactory ,創建MetaHolder ,MetaHolder 裏面涵蓋了所有的 必須的信息
  4. 獲取執行方式 executionType
  5. CommandExecutor.execute(invokable, executionType, metaHolder);先通過castToExecutable轉換成HystrixExecutable類型 ,再執行運行execute();
  6. 接下來 主要的重點就是 execute()
  7. execute() 方法 就是 queue().get(); 接下來 是RxJava 的編程風格 ,就挑選主要的重點
  8. queue() 還是 調用了 final Future delegate = toObservable().toBlocking().toFuture();

關於RxJava,解釋一些基本的概念
核心思想和Java的觀察者模式非常像:被觀察者和觀察者通過訂閱產生一種關係,當被觀察者發生一些改變,通知觀察者,觀察者對應做出相應的迴應。
主要有三個關鍵詞需要記住:被觀察者(Observable),訂閱(subscribe),觀察者(Observer)

方法介紹:
R execute():同步執行,從依賴服務得到單一結果對象.
Future queue():異步執行,返回一個 Future 以便獲取執行結果,也是單一結果對象.
Observable observe():創建Observable後會訂閱Observable,可以返回多個結果.
Observable toObservable():返回一個Observable,只有訂閱時纔會執行,可以返回多個結果.
Defer 操作符會一直等待直到有觀察者訂閱它,然後它使用Observable工廠方法生成一個Observable

二、運行流程

2.1 toObservable()方法

public Observable<R> toObservable() {
        final AbstractCommand<R> _cmd = this;    
           xxx...xxxx    
        final Func0<Observable<R>> applyHystrixSemantics = new Func0<Observable<R>>() {
            @Override
            public Observable<R> call() {
                if (commandState.get().equals(CommandState.UNSUBSCRIBED)) {
                    return Observable.never();
                }
                return applyHystrixSemantics(_cmd);
            }
        };

        return Observable.defer(new Func0<Observable<R>>() {
            @Override
            public Observable<R> call() {
                 xxx...xxxx    
                Observable<R> hystrixObservable =
                        Observable.defer(applyHystrixSemantics)
                                .map(wrapWithAllOnNextHooks);
                Observable<R> afterCache;
                // put in cache
                if (requestCacheEnabled && cacheKey != null) {
                    // wrap it for caching
                    HystrixCachedObservable<R> toCache = HystrixCachedObservable.from(hystrixObservable, _cmd);
                    HystrixCommandResponseFromCache<R> fromCache = (HystrixCommandResponseFromCache<R>) requestCache.putIfAbsent(cacheKey, toCache);
                    if (fromCache != null) {
                        // another thread beat us so we'll use the cached value instead
                        toCache.unsubscribe();
                        isResponseFromCache = true;
                        return handleRequestCacheHitAndEmitValues(fromCache, _cmd);
                    } else {
                        // we just created an ObservableCommand so we cast and return it
                        afterCache = toCache.toObservable();
                    }
                } else {
                    afterCache = hystrixObservable;
                }
                return afterCache
                        .doOnTerminate(terminateCommandCleanup)     // perform cleanup once (either on normal terminal state (this line), or unsubscribe (next line))
                        .doOnUnsubscribe(unsubscribeCommandCleanup) // perform cleanup once
                        .doOnCompleted(fireOnCompletedHook);
            }
        });
    }

toObservable() 方法 return Observable.defer ,在call 方法裏面 的 afterCache 又是Observable.defer(applyHystrixSemantics),所以定位到 方法applyHystrixSemantics(_cmd);

2.2 applyHystrixSemantics()方法

private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
  ... xxx  ... xxx
        // 判斷斷路器是否開啓 ,這裏也有好幾種情況
        if (circuitBreaker.attemptExecution()) {
        // 獲取對應的方式,默認是線程隔離
            final TryableSemaphore executionSemaphore = getExecutionSemaphore();
            final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false);
            final Action0 singleSemaphoreRelease = new Action0() {
                @Override
                public void call() {
                    if (semaphoreHasBeenReleased.compareAndSet(false, true)) {
                        executionSemaphore.release();
                    }
                }
            };
           //判斷是否可以訪問,如果是線程隔離,默認是true ,如果是信號量,需要判斷當前是否可以,具體邏輯在下面
            if (executionSemaphore.tryAcquire()) {
                try {
                    /* used to track userThreadExecutionTime */
                    executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis());
                    return executeCommandAndObserve(_cmd)
                            .doOnError(markExceptionThrown)
                            .doOnTerminate(singleSemaphoreRelease)
                            .doOnUnsubscribe(singleSemaphoreRelease);
                } catch (RuntimeException e) {
                    return Observable.error(e);
                }
            } else {
                return handleSemaphoreRejectionViaFallback();
            }
        } else {
            //直接進入Fallback處理邏輯。
            return handleShortCircuitViaFallback();
        }
    }

獲取當前的隔離type ,然後再判斷tryAcquire() 是否有權限,如果是信號量隔離,邏輯在下面 ,如果是 TryableSemaphoreNoOp ,tryAcquire永遠是 true, 我們繼續分析線程池隔離,代碼追蹤如下:
在這裏插入圖片描述

2.3 TryableSemaphore 接口

TryableSemaphore 接口裏面的 方法比較 簡單,就是 一個判斷是否可以訪問,一個釋放資源,一個獲取當前的使用的個數

static interface TryableSemaphore {
        public abstract boolean tryAcquire();
        public abstract void release();
        public abstract int getNumberOfPermitsUsed();
    }

從結構上可以看出 被 TryableSemaphore 被兩個實現了,一個是 TryableSemaphoreActual,一個是TryableSemaphoreNoOp,TryableSemaphoreNoOp 是沒有操作的(tryAcquire()永遠true),主要看一下TryableSemaphoreActual
在這裏插入圖片描述

2.3.1 TryableSemaphoreActual 信號量

static class TryableSemaphoreActual implements TryableSemaphore {
        protected final HystrixProperty<Integer> numberOfPermits;
        private final AtomicInteger count = new AtomicInteger(0);

        public TryableSemaphoreActual(HystrixProperty<Integer> numberOfPermits) {
            this.numberOfPermits = numberOfPermits;
        }
         //先+1 ,然後 和numberOfPermits (默認10個)比較大小,
         // 大於numberOfPermits  ,減1 並返回 false,反之返回 true
        @Override
        public boolean tryAcquire() {
            int currentCount = count.incrementAndGet();
            if (currentCount > numberOfPermits.get()) {
                count.decrementAndGet();
                return false;
            } else {
                return true;
            }
        }
        @Override
        public void release() {
            count.decrementAndGet();
        }
        @Override
        public int getNumberOfPermitsUsed() {
            return count.get();
        }

    }

主要邏輯就是:

  1. 當前數量 先+1 ,然後 和numberOfPermits (默認10個)比較大小
  2. 大於numberOfPermits ,減1 並返回 false
  3. 反之返回 true
    信號量的判斷 ,如果是false ,就走拒絕策略,同時也可以看出 信號量的判斷,沒有請求時間長短的過濾判斷

2.4 線程池相關

在上面我們已經定位到了 getUserExecutionObservable方法,主要就是HystrixCommand的 run() 方法,這裏的具體邏輯在

    @Override
    final protected Observable<R> getExecutionObservable() {
        return Observable.defer(new Func0<Observable<R>>() {
            @Override
            public Observable<R> call() {
                try {
                    return Observable.just(run());
                } catch (Throwable ex) {
                    return Observable.error(ex);
                }
            }
        }).doOnSubscribe(new Action0() {
            @Override
            public void call() {
                // Save thread on which we get subscribed so that we can interrupt it later if needed
                executionThread.set(Thread.currentThread());
            }
        });
    }

在這裏插入圖片描述
在這裏插入圖片描述

2.4.1 threadPool

threadPool 是 AbstractCommand 的構造函數 在 實例的時候 直接初始化了,具體方法爲:

  this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);

  private static HystrixThreadPool initThreadPool(HystrixThreadPool fromConstructor, HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults) {
        if (fromConstructor == null) {
            // get the default implementation of HystrixThreadPool
            return HystrixThreadPool.Factory.getInstance(threadPoolKey, threadPoolPropertiesDefaults);
        } else {
            return fromConstructor;
        }
    }

在獲取數據的時候,用了一個 Map做了緩存,key 就是 threadPoolKey,Value 就是 HystrixThreadPool

final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();

static HystrixThreadPool getInstance(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesBuilder) {
            // get the key to use instead of using the object itself so that if people forget to implement equals/hashcode things will still work
            String key = threadPoolKey.name();

            // this should find it for all but the first time
            HystrixThreadPool previouslyCached = threadPools.get(key);
            if (previouslyCached != null) {
                return previouslyCached;
            }

            // if we get here this is the first time so we need to initialize
            synchronized (HystrixThreadPool.class) {
                if (!threadPools.containsKey(key)) {
                    threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
                }
            }
            return threadPools.get(key);
        }

分析到這裏 ,線程池隔離的大致流程也已經清晰

  1. 在實例AbstractCommand 的時候,就會根據 配置的線程池參數創建一個線程大小,同時制定 一個 threadPoolKey ,沒有設置就是方法名稱,
  2. 存放到 對應的內存緩存裏面
  3. 調用的時候就從裏面取

這裏分析了 線程池相關的,下面貼一張 官方原圖,
Hystrix 的官方地址在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章