当使用Spring Cloud中的Feign来进行远程服务调用时,有时需要为Feign Client配置fallbackFactory
,以处理远程服务调用失败的情况。然而,在某些情况下,即使配置了fallbackFactory
,异常代码似乎无法进入对应的fallbackFactory
类的方法。为了解决这个问题,让我们深入了解一下Feign的工作原理以及可能的解决方法。
Feign 的工作原理
首先,让我们了解一下Feign的工作机制。在Spring Cloud中,我们使用@FeignClient
注解来标记一个Feign客户端接口。通过使用@EnableFeignClients
来启用Feign客户端的功能。启用Feign客户端后,Spring会使用FeignClientsRegistrar
类来扫描被@FeignClient
标记的接口,并将其注册为Bean定义。在FeignClientFactoryBean
中,通过使用FactoryBean
的方式来创建Feign代理类。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
/**
* Define a fallback factory for the specified Feign client interface. The fallback
* factory must produce instances of fallback classes that implement the interface
* annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.
*
* @see feign.hystrix.FallbackFactory for details.
* @return fallback factory for the specified Feign client interface
*/
Class<?> fallbackFactory() default void.class;
}
我们需要在启动类上添加注解@EnableFeignClients
并指明扫描路径
less
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients{}
@EnableFeignClients
会@Import
FeignClientsRegistrar
FeignClientsRegistrar
会扫描 basePackage 的被 FeignClient
修饰的类
java
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册配置实例到 spring
registerDefaultConfiguration(metadata, registry);
// 注册 feign 客户端
registerFeignClients(metadata, registry);
}
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 扫描被@FeignClinet 标记的 class
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// 拿到EnableFeignClients注解的配置
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
...
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 注册 feign 客户端
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
// 创建 beanDefinition
String className = annotationMetadata.getClassName();
// 设置 beanClass = FeignClientFactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
...
设置属性...
...
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
// 注册 beanDefiition 到 spring 容器
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
创建 bean 定义的时候有一步很关键,指定了 beanClass=FeignClientFactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder.*genericBeanDefinition*(FeignClientFactoryBean.*class*);
FeignClientFactoryBean
是个FactoryBean
,在后续 springbean实例化的时候,将由这个FactoryBean
生成动态代理类*
我们再看看FeignClientFactoryBean
的 getObject()
java
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// 这个 builder 很重要
Feign.Builder builder = feign(context);
...
// 获取到 target ,然后调用 target 方法
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
}
FeignClientsConfiguration
中的 Feign.Builder
有两个实现Feign.Builder
默认是Feign.Builder
,Targeter
也有两个实现,默认是HystrixTargeter
java
// 默认的 builder
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
// hystrixBuilder
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
// 默认使用HystrixTargeter
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
代理对象的创建
因为默认的 target 是 HystrixTargeter
我们再看看HystrixTargeter
的target
方法
java
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 如果不是HystrixFeign.Builder 那么按默认的target 按默认的实现
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
// 如果有FallbackFactory 就
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
}
如果不是 feign.hystrix.HystrixFeign.Builder
则使用默认的FeignInvocationHandler
创建动态代理类
java
public final class HystrixFeign {
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
// 配置 fallbackFacotory 然后创建实例
return build(fallbackFactory).newInstance(target);
}
/** Configures components needed for hystrix integration. */
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
// 动态代理的 InvocationHandler
return new HystrixInvocationHandler(target, dispatch, setterFactory,
nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
}
FeignInvocationHandler
的 invoke 方法
java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
//根据Method元数据,从dispatch找到对应的SynchronousMethodHandler,然后执行http调用
return dispatch.get(method).invoke(args);
}
SynchronousMethodHandler,执行 http 请求,异常后重试 targetRequest方法里面包括有 feign调用拦截器的逻辑
java
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
try {
//异常重试
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
// 重试后会抛出异常
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template) throws Throwable {
// 这里会执行 feign 的拦截器
Request request = targetRequest(template);
//发送http 请求
...
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
默认的实现,当出现异常的时候重试,如果再失败会抛出异常 targetWithFallbackFactory 的实现
java
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
HystrixInvocationHandler
的 invoke 方法,会创建HystrixCommand
实例,并执行 execute方法
java
HystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
SetterFactory setterFactory, FallbackFactory<?> fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
this.setterMethodMap = toSetters(setterFactory, target, dispatch.keySet());
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
// Object 的方法按默认的执行
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
//根据Method元数据,从dispatch找到对应的SynchronousMethodHandler,然后执行http调用
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
@Override
protected Object getFallback() {
if (fallbackFactory == null) {
// 没有做降级处理,直接抛出异常
return super.getFallback();
}
try {
// getExecutionException()用于解析异常,返回的fallback就是降级对象
Object fallback = fallbackFactory.create(getExecutionException());
// 执行降级逻辑
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
// 根据方法返回类型作不同处理
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
}
...
} 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());
}
}
};
// 根据方法返回类型作不同处理
if (Util.isDefault(method)) {
return hystrixCommand.execute();
}
...
// 最后执行
return hystrixCommand.execute();
}
HystrixCommand
实例化的时候会调用父类的构造AbstractCommand
java
protected AbstractCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
//将命令分组在一起,例如报告、警报、仪表板(feign class 名字)
this.commandGroup = initGroupKey(group);
//监控、断路器、指标发布、缓存和其他此类用途(方法名)
this.commandKey = initCommandKey(key, getClass());
// 一些配置属性,数据统计窗口,断路器配置等
this.properties = initCommandProperties(this.commandKey, propertiesStrategy, commandPropertiesDefaults);
// 线程池的 key
this.threadPoolKey = initThreadPoolKey(threadPoolKey, this.commandGroup, this.properties.executionIsolationThreadPoolKeyOverride().get());
// 统计
this.metrics = initMetrics(metrics, this.commandGroup, this.threadPoolKey, this.commandKey, this.properties);
// 断路器
this.circuitBreaker = initCircuitBreaker(this.properties.circuitBreakerEnabled().get(), circuitBreaker, this.commandGroup, this.commandKey, this.properties, this.metrics);
// 初始化线程池,没有就创建一个
this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);
//Strategies from plugins
this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(this.commandKey, this.commandGroup, this.metrics, this.circuitBreaker, this.properties);
this.executionHook = initExecutionHook(executionHook);
this.requestCache = HystrixRequestCache.getInstance(this.commandKey, this.concurrencyStrategy);
this.currentRequestLog = initRequestLog(this.properties.requestLogEnabled().get(), this.concurrencyStrategy);
/* fallback semaphore override if applicable */
this.fallbackSemaphoreOverride = fallbackSemaphore;
/* execution semaphore override if applicable */
this.executionSemaphoreOverride = executionSemaphore;
}
java
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
execute方法会返回 Future类型的 run()
的成功结果或者getFallback()
的失败结果,当远程调用失败的时候,会回调getFallback
方法,找到配置的对应的 fallbackFactory
fallbackFactory怎么生效
看了上面的代码分析,fallbackFactory
是否生效,关键在于HystrixTargeter
的 target
方法的执行,对应就是Feign.Builder
实例是否是HystrixFeign.builder()
。
总结:为了使@FeignClient
配置的fallbackFactory
生效,需要引入Hystrix依赖。在项目的依赖配置文件中,添加以下依赖:
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
然后,在项目的配置文件中,添加以下配置,启用Feign的Hystrix支持:
yaml
feign:
hystrix:
enabled: true
这些配置将启用Hystrix,并使Feign使用HystrixInvocationHandler
来创建动态代理类。这样,即使在远程服务调用失败的情况下,配置的fallbackFactory
也会生效,从而能够执行对应的fallbackFactory
方法,实现容错处理。
在实际使用中,可以根据需要配置fallbackFactory
,并在远程服务调用失败时提供适当的降级处理逻辑,以确保系统的可靠性和稳定性。
希望这篇文章对您理解Feign的工作原理以及如何配置fallbackFactory
有所帮助。如果您有任何问题或需要进一步的解释,请随时提出。