hystrix源碼解析——FallbackMethod是如何接收異常的

一、何時觸發fallbackMethod

FAILURE:執行失敗,拋出異常。
TIMEOUT:執行超時。
SHORT_CIRCUITED:斷路器打開。
THREAD_POOL_REJECTED:線程池拒絕。
SEMAPHORE_REJECTED:信號量拒絕。

注意:並不是所有的異常都會觸發fallbackMethod,下面的這些異常或其子類會直接拋出

1. @HystrixCommand中定義的被忽略的異常ignoreExceptions,會被封裝在HystrixBadRequestException中拋出

HystrixBadRequestException

2. 繼承了ExceptionNotWrappedByHystrix的異常

ExceptionNotWrappedByHystrix

3. 無法恢復的系統異常

StackOverflowError
VirtualMachineError
ThreadDeath
LinkageError

二、如何接收異常

1. fallbackMethod不接收異常

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "hiError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
    }

    public String hiError(String name) {
        return "hi,"+name+",sorry,error!";
    }

}

2. fallbackMethod接收異常

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "hiError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
    }

    public String hiError(String name, Throwable exception) {
        return "hi,"+name+",sorry,error: " + exception.getMessage();
    }

}

三、源碼解析

1. 執行主函數,如果失敗的話調用回調函數

abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {

...

	/**
     * This decorates "Hystrix" functionality around the run() Observable.
     *
     * @return R
     */
    private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
        final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread();

        final Action1<R> markEmits = new Action1<R>() {
            @Override
            public void call(R r) {
                if (shouldOutputOnNextEvents()) {
                    executionResult = executionResult.addEvent(HystrixEventType.EMIT);
                    eventNotifier.markEvent(HystrixEventType.EMIT, commandKey);
                }
                if (commandIsScalar()) {
                    long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
                    eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList());
                    eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey);
                    executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS);
                    circuitBreaker.markSuccess();
                }
            }
        };

        final Action0 markOnCompleted = new Action0() {
            @Override
            public void call() {
                if (!commandIsScalar()) {
                    long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
                    eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList());
                    eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey);
                    executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS);
                    circuitBreaker.markSuccess();
                }
            }
        };

		//如果失敗的話,調用回調函數
        final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
            @Override
            public Observable<R> call(Throwable t) {
                Exception e = getExceptionFromThrowable(t);
                executionResult = executionResult.setExecutionException(e);
                if (e instanceof RejectedExecutionException) {
                    return handleThreadPoolRejectionViaFallback(e);
                } else if (t instanceof HystrixTimeoutException) {
                    return handleTimeoutViaFallback();
                } else if (t instanceof HystrixBadRequestException) {
                    return handleBadRequestByEmittingError(e);
                } else {
                    /*
                     * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
                     */
                    if (e instanceof HystrixBadRequestException) {
                        eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
                        return Observable.error(e);
                    }

                    return handleFailureViaFallback(e);
                }
            }
        };

        final Action1<Notification<? super R>> setRequestContext = new Action1<Notification<? super R>>() {
            @Override
            public void call(Notification<? super R> rNotification) {
                setRequestContextIfNeeded(currentRequestContext);
            }
        };

        Observable<R> execution;
        if (properties.executionTimeoutEnabled().get()) {
            execution = executeCommandWithSpecifiedIsolation(_cmd)
                    .lift(new HystrixObservableTimeoutOperator<R>(_cmd));
        } else {
            execution = executeCommandWithSpecifiedIsolation(_cmd);
        }

        return execution.doOnNext(markEmits)
                .doOnCompleted(markOnCompleted)
                .onErrorResumeNext(handleFallback)
                .doOnEach(setRequestContext);
    }
	
...

}

2. 檢查是否存在擴展的回調函數(帶異常參數)

public final class MethodProvider {

...

	private static abstract class FallbackMethodFinder {
        FallbackMethodFinder next;

        public FallbackMethodFinder() {
        }

        public FallbackMethodFinder(FallbackMethodFinder next) {
            this.next = next;
        }

        boolean isDefault() {
            return false;
        }

        boolean isSpecific(){
            return false;
        }

        public abstract String getFallbackName(Class<?> enclosingType, Method commandMethod);

        public FallbackMethod find(Class<?> enclosingType, Method commandMethod, boolean extended) {
            if (canHandle(enclosingType, commandMethod)) {
                return doFind(enclosingType, commandMethod, extended);
            } else if (next != null) {
                return next.find(enclosingType, commandMethod, extended);
            } else {
                return FallbackMethod.ABSENT;
            }
        }

        abstract boolean canHandle(Class<?> enclosingType, Method commandMethod);

        private FallbackMethod doFind(Class<?> enclosingType, Method commandMethod, boolean extended) {
            String name = getFallbackName(enclosingType, commandMethod);
            Class<?>[] fallbackParameterTypes = null;
            if (isDefault()) {
                fallbackParameterTypes = new Class[0];
            } else {
                fallbackParameterTypes = commandMethod.getParameterTypes();
            }

            if (extended && fallbackParameterTypes[fallbackParameterTypes.length - 1] == Throwable.class) {
                fallbackParameterTypes = ArrayUtils.remove(fallbackParameterTypes, fallbackParameterTypes.length - 1);
            }

			// -------關鍵代碼:嘗試獲取擴展的回調函數(帶異常參數)-------
            Class<?>[] extendedFallbackParameterTypes = Arrays.copyOf(fallbackParameterTypes, fallbackParameterTypes.length + 1);
            extendedFallbackParameterTypes[fallbackParameterTypes.length] = Throwable.class;

            Optional<Method> exFallbackMethod = getMethod(enclosingType, name, extendedFallbackParameterTypes);
			// ------------------------------------------------
			
            Optional<Method> fMethod = getMethod(enclosingType, name, fallbackParameterTypes);
            Method method = exFallbackMethod.or(fMethod).orNull();
            if (method == null) {
                throw new FallbackDefinitionException("fallback method wasn't found: " + name + "(" + Arrays.toString(fallbackParameterTypes) + ")");
            }
			
			// exFallbackMethod.isPresent()的含義爲:是否爲擴展的回調函數(帶異常參數)
            return new FallbackMethod(method, exFallbackMethod.isPresent(), isDefault());
        }

    }
	
...

}

3. 將FallbackMethod的extended標誌位一直傳遞到MetaHolder的extendedFallback標誌位

public class FallbackMethod {

...

    private final Method method;
    private final boolean extended; // 是否爲擴展的回調函數(帶異常參數)
    private final boolean defaultFallback;
    private ExecutionType executionType;

    public FallbackMethod(Method method, boolean extended, boolean defaultFallback) {
        this.method = method;
        this.extended = extended;
        this.defaultFallback = defaultFallback;
        if (method != null) {
            this.executionType = ExecutionType.getExecutionType(method.getReturnType());
        }
    }
	
...

}

4. 如果是擴展的回調函數,將異常追加爲最後一個參數

public final class CommonUtils {

    private CommonUtils(){

    }

    public static Object[] createArgsForFallback(MetaHolder metaHolder, Throwable exception) {
        return createArgsForFallback(metaHolder.getArgs(), metaHolder, exception);
    }

    public static Object[] createArgsForFallback(Object[] args, MetaHolder metaHolder, Throwable exception) {
	
		// 回調函數是否爲擴展的回調函數(帶異常參數)
        if (metaHolder.isExtendedFallback()) {
            if (metaHolder.isExtendedParentFallback()) {
                args[args.length - 1] = exception;
            } else {
                args = Arrays.copyOf(args, args.length + 1);
                args[args.length - 1] = exception;
            }
        } else {
            if (metaHolder.isExtendedParentFallback()) {
                args = ArrayUtils.remove(args, args.length - 1);
            }
        }
        return args;
    }
}

5. 調用回調函數

abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {

...

	private Observable<R> getFallbackOrThrowException(final AbstractCommand<R> _cmd, final HystrixEventType eventType, final FailureType failureType, final String message, final Exception originalException) {
        final HystrixRequestContext requestContext = HystrixRequestContext.getContextForCurrentThread();
        long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
        // record the executionResult
        // do this before executing fallback so it can be queried from within getFallback (see See https://github.com/Netflix/Hystrix/pull/144)
        executionResult = executionResult.addEvent((int) latency, eventType);

        if (shouldNotBeWrapped(originalException)){
            /* executionHook for all errors */
            Exception e = wrapWithOnErrorHook(failureType, originalException);
            return Observable.error(e);
        } else if (isUnrecoverable(originalException)) {
            logger.error("Unrecoverable Error for HystrixCommand so will throw HystrixRuntimeException and not apply fallback. ", originalException);

            /* executionHook for all errors */
            Exception e = wrapWithOnErrorHook(failureType, originalException);
            return Observable.error(new HystrixRuntimeException(failureType, this.getClass(), getLogMessagePrefix() + " " + message + " and encountered unrecoverable error.", e, null));
        } else {
            if (isRecoverableError(originalException)) {
                logger.warn("Recovered from java.lang.Error by serving Hystrix fallback", originalException);
            }

            if (properties.fallbackEnabled().get()) {
                /* fallback behavior is permitted so attempt */

                final Action1<Notification<? super R>> setRequestContext = new Action1<Notification<? super R>>() {
                    @Override
                    public void call(Notification<? super R> rNotification) {
                        setRequestContextIfNeeded(requestContext);
                    }
                };

                final Action1<R> markFallbackEmit = new Action1<R>() {
                    @Override
                    public void call(R r) {
                        if (shouldOutputOnNextEvents()) {
                            executionResult = executionResult.addEvent(HystrixEventType.FALLBACK_EMIT);
                            eventNotifier.markEvent(HystrixEventType.FALLBACK_EMIT, commandKey);
                        }
                    }
                };

                final Action0 markFallbackCompleted = new Action0() {
                    @Override
                    public void call() {
                        long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
                        eventNotifier.markEvent(HystrixEventType.FALLBACK_SUCCESS, commandKey);
                        executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_SUCCESS);
                    }
                };

                final Func1<Throwable, Observable<R>> handleFallbackError = new Func1<Throwable, Observable<R>>() {
                    @Override
                    public Observable<R> call(Throwable t) {
                        Exception e = originalException;
                        Exception fe = getExceptionFromThrowable(t);

                        if (fe instanceof UnsupportedOperationException) {
                            long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
                            logger.debug("No fallback for HystrixCommand. ", fe); // debug only since we're throwing the exception and someone higher will do something with it
                            eventNotifier.markEvent(HystrixEventType.FALLBACK_MISSING, commandKey);
                            executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_MISSING);

                            /* executionHook for all errors */
                            e = wrapWithOnErrorHook(failureType, e);

                            return Observable.error(new HystrixRuntimeException(failureType, _cmd.getClass(), getLogMessagePrefix() + " " + message + " and no fallback available.", e, fe));
                        } else {
                            long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
                            logger.debug("HystrixCommand execution " + failureType.name() + " and fallback failed.", fe);
                            eventNotifier.markEvent(HystrixEventType.FALLBACK_FAILURE, commandKey);
                            executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_FAILURE);

                            /* executionHook for all errors */
                            e = wrapWithOnErrorHook(failureType, e);

                            return Observable.error(new HystrixRuntimeException(failureType, _cmd.getClass(), getLogMessagePrefix() + " " + message + " and fallback failed.", e, fe));
                        }
                    }
                };

                final TryableSemaphore fallbackSemaphore = getFallbackSemaphore();
                final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false);
                final Action0 singleSemaphoreRelease = new Action0() {
                    @Override
                    public void call() {
                        if (semaphoreHasBeenReleased.compareAndSet(false, true)) {
                            fallbackSemaphore.release();
                        }
                    }
                };

                Observable<R> fallbackExecutionChain;

                // acquire a permit
                if (fallbackSemaphore.tryAcquire()) {
                    try {
                        if (isFallbackUserDefined()) {
                            executionHook.onFallbackStart(this);
                            fallbackExecutionChain = getFallbackObservable();
                        } else {
                            //same logic as above without the hook invocation
                            fallbackExecutionChain = getFallbackObservable();
                        }
                    } catch (Throwable ex) {
                        //If hook or user-fallback throws, then use that as the result of the fallback lookup
                        fallbackExecutionChain = Observable.error(ex);
                    }

                    return fallbackExecutionChain
                            .doOnEach(setRequestContext)
                            .lift(new FallbackHookApplication(_cmd))
                            .lift(new DeprecatedOnFallbackHookApplication(_cmd))
                            .doOnNext(markFallbackEmit)
                            .doOnCompleted(markFallbackCompleted)
                            .onErrorResumeNext(handleFallbackError)
                            .doOnTerminate(singleSemaphoreRelease)
                            .doOnUnsubscribe(singleSemaphoreRelease);
                } else {
                   return handleFallbackRejectionByEmittingError();
                }
            } else {
                return handleFallbackDisabledByEmittingError(originalException, failureType, message);
            }
        }
    }
	
	
	
	
    protected boolean shouldNotBeWrapped(Throwable underlying) {
        return underlying instanceof ExceptionNotWrappedByHystrix;
    }

    /**
     * Returns true iff the t was caused by a java.lang.Error that is unrecoverable.  Note: not all java.lang.Errors are unrecoverable.
     * @see <a href="https://github.com/Netflix/Hystrix/issues/713"></a> for more context
     * Solution taken from <a href="https://github.com/ReactiveX/RxJava/issues/748"></a>
     *
     * The specific set of Error that are considered unrecoverable are:
     * <ul>
     * <li>{@code StackOverflowError}</li>
     * <li>{@code VirtualMachineError}</li>
     * <li>{@code ThreadDeath}</li>
     * <li>{@code LinkageError}</li>
     * </ul>
     *
     * @param t throwable to check
     * @return true iff the t was caused by a java.lang.Error that is unrecoverable
     */
    private boolean isUnrecoverable(Throwable t) {
        if (t != null && t.getCause() != null) {
            Throwable cause = t.getCause();
            if (cause instanceof StackOverflowError) {
                return true;
            } else if (cause instanceof VirtualMachineError) {
                return true;
            } else if (cause instanceof ThreadDeath) {
                return true;
            } else if (cause instanceof LinkageError) {
                return true;
            }
        }
        return false;
    }
	
...

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