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有所帮助。如果您有任何问题或需要进一步的解释,请随时提出。

相关推荐
程序员爱钓鱼1 小时前
Go语言实战案例-创建模型并自动迁移
后端·google·go
javachen__1 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
uzong6 小时前
技术故障复盘模版
后端
GetcharZp7 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程7 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研7 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi8 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国9 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy9 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack9 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt