feign fallbackFactory不生效问题

当使用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.BuilderTargeter 也有两个实现,默认是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我们再看看HystrixTargetertarget 方法

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 是否生效,关键在于HystrixTargetertarget 方法的执行,对应就是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有所帮助。如果您有任何问题或需要进一步的解释,请随时提出。

相关推荐
monkey_meng18 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马21 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng25 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
paopaokaka_luck5 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风6 小时前
详解K8S--声明式API
后端
Peter_chq6 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml47 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~7 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616887 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
睡觉谁叫~~~8 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust