异常:
│ com.netflix.hystrix.exception.HystrixRuntimeException: XxxClient#getXxx() failed and fallback failed.
│ at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:832)
│ at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:807)
│ at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)
│ at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1397)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at rx.observers.Subscribers$5.onError(Subscribers.java:230)
│ at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:44)
│ at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:28)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
│ at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
│ at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
│ at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
│ at rx.Observable.unsafeSubscribe(Observable.java:10327)
│ at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:142)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$3.onError(AbstractCommand.java:1194)
│ at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.onError(OperatorSubscribeOn.java:80)
│ at rx.observers.Subscribers$5.onError(Subscribers.java:230)
│ at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
│ at rx.observers.Subscribers$5.onError(Subscribers.java:230)
│ at com.netflix.hystrix.AbstractCommand$DeprecatedOnRunHookApplication$1.onError(AbstractCommand.java:1431)
│ at com.netflix.hystrix.AbstractCommand$ExecutionHookApplication$1.onError(AbstractCommand.java:1362)
问题产生:
通过spring openfeign的FallbackFactory对feign接口进行降级熔断操作;具体问题产生原因是FallbackFactory#create创建的对象,通过反射调用相关方法时,方法内部抛出了 Runtime Exception,也就是说并非降级操作不起作用,而是写的降级方法本身存在问题!比如 方法內抛出了异常、属性注入失败等等
OpenFeign提供的两种降级操作:
1、fallback
@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class Fallback implements TestClient {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
这种方式最简单,当feign接口调用失败时直接走fallback方法,不足之处在于拿不到具体feign调用失败的err信息
2、fallbackFactory
@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",
fallbackFactory = TestFallbackFactory.class)
protected interface TestClientWithFactory {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class TestFallbackFactory implements FallbackFactory<FallbackWithFactory> {
@Override
public FallbackWithFactory create(Throwable cause) {
return new FallbackWithFactory();
}
}
static class FallbackWithFactory implements TestClientWithFactory {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
通过这种方式就可以在创建fallback实体对象的时候把throwable错误信息传回来
3、fallback与fallbackFactory的差别:
两者最终的处理方式都是一致的,fallbackFactory在真正使用的时候才创建需要的fallback对象,这个时候就很方便的将throwable信息传回;fallback最终也会使用默认的工厂方法:
/** Returns a constant fallback after logging the cause to FINE level. */
final class Default<T> implements FallbackFactory<T> {
// jul to not add a dependency
final Logger logger;
final T constant;
public Default(T constant) {
this(constant, Logger.getLogger(Default.class.getName()));
}
Default(T constant, Logger logger) {
this.constant = checkNotNull(constant, "fallback");
this.logger = checkNotNull(logger, "logger");
}
@Override
public T create(Throwable cause) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "fallback due to: " + cause.getMessage(), cause);
}
return constant;
}
@Override
public String toString() {
return constant.toString();
}
}
当然这个默认的工厂方法创建的fallback对象是没有携带throwable信息的
来看看两者最终都会调用fallback的方法:
feign.hystrix.HystrixInvocationHandler#invoke
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
.......
HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
.......
@Override
protected Object getFallback() {
if (fallbackFactory == null) {
return super.getFallback();
}
try {
// 如果要走fallback的话,最终都就走到这里来
// fallbackFactory要么是默认的,要么是自定义的,也就是对应的以上两种定义fallback的方式
Object fallback = fallbackFactory.create(getExecutionException());
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return ((Observable) result).toBlocking().first();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return ((Single) result).toObservable().toBlocking().first();
} else if (isReturnsCompletable(method)) {
((Completable) result).await();
return null;
} else if (isReturnsCompletableFuture(method)) {
return ((Future) result).get();
} else {
return result;
}
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
} catch (InvocationTargetException | ExecutionException e) {
// Exceptions on fallback are tossed by Hystrix
throw new AssertionError(e.getCause());
} catch (InterruptedException e) {
// Exceptions on fallback are tossed by Hystrix
Thread.currentThread().interrupt();
throw new AssertionError(e.getCause());
}
}
};
........
简单来说: 两种降级操作 fallback 和 fallbackFactory的处理方式是一致的,如果想要拿到具体的异常信息就使用 fallbackFactory
调试版本: org.springframework.cloud:spring-cloud-openfeign:2.2.5.RELEASE 以及其依赖的io.github.openfeign:feign-hystrix:10.10.1
以上是个人理解,如有问题请指出!