一、何時觸發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;
}
...
}