Springcloud - Feign

454 阅读8分钟
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.0.2</version>
</dependency>

0.本文重点

  1. FeignClient创建代理对象过程
  2. FeignClient执行调用过程
  3. FeignClient如何给每个client单独配置超时时间
  4. 当发生404,502,503时如何使用Retry
  5. 引申:使用Supplier和FactoryBean的区别

1.注册beanDefinition

EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}

FeignClientsRegistrar#registerBeanDefinitions

class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            registerDefaultConfiguration(metadata, registry);
            registerFeignClients(metadata, registry);
    }

}

在前文spring ioc处理机制一文中我们知道@Import注解声明的ImportBeanDefinitionRegistrar会在ConfigurationClassPostProcessor#processConfigBeanDefinitions扫描beanDefinition时回调其registerBeanDefinitions方法。

FeignClientsRegistrar#registerFeignClients

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) { 
            //获取类路径扫描器
            ClassPathScanningCandidateComponentProvider scanner = getScanner();
            scanner.setResourceLoader(this.resourceLoader);
           /添加扫描 注解过滤的filter  代表 扫描类时我只要 带FeignClient注解的
            scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
            Set<String> basePackages = getBasePackages(metadata);
              //声明了多个包路径 一个个扫
            for (String basePackage : basePackages) {
              //内部调用ClassPathScanningCandidateComponentProvider#scanCandidateComponents返回符合要求
             //的beanDefinition
 candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
            }
    }
    else {
            //指定class 类型,只实例化相应的clients,我们一般都是直接指定包路径
            for (Class<?> clazz : clients) {
                    candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
            }
    }    
    for (BeanDefinition candidateComponent : candidateComponents) {
        if (candidateComponent instanceof AnnotatedBeanDefinition) {
                //强制要求必须为接口
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(),
                                "@FeignClient can only be specified on an interface");
                //获取注解元信息
                Map<String, Object> attributes = annotationMetadata
                                .getAnnotationAttributes( FeignClient.class.getCanonicalName());

                String name = getClientName(attributes);
                registerClientConfiguration(registry, name,
                                attributes.get("configuration"));
                //基于注解元信息 改造beandefition   definition中的参数决定了spring如何实例化bean,
                //采用何种方式,有什么样的初始值,使用何种方式注入依赖等
                registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}

FeignClientsRegistrar#getScanner

protected ClassPathScanningCandidateComponentProvider getScanner() {
//Spring提供的这个类 可以帮助我们扫描classPath下类信息  重要:我们可以合理的运用这个类 来自定义自己的框架
//这里复写了 isCandidateComponent方法 声明扫出来的类不能是一个注解
   return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
      @Override
      //父类的实现是必须为非接口或非抽象类(如果为抽象类必须有包含@Lookup注解的方法)
      protected boolean isCandidateComponent(
            AnnotatedBeanDefinition beanDefinition) {
         boolean isCandidate = false;
         if (beanDefinition.getMetadata().isIndependent()) {
            //非注解
            if (!beanDefinition.getMetadata().isAnnotation()) {
               isCandidate = true;
            }
         }
         return isCandidate;
      }
   };
}

ClassPathScanningCandidateComponentProvider#scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      //classpath*:xxx/xx/xx/**/*.class
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      //扫描出来的文件资源
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      ...
      for (Resource resource : resources) {
          ...
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               //这里除了之前声明的只扫描带FeignClient注解的类 还会进行Conditional 通用判断
               if (isCandidateComponent(metadataReader)) {
               //继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口
               //通常这类definition用于 描述标注 @Component 注解的 Bean 
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setSource(resource);
                  //这里就是上面重写的方法了
                  if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);
                  }
         .....
   return candidates;
}

FeignClientsRegistrar#registerFeignClient

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
      Map<String, Object> attributes) {
   String className = annotationMetadata.getClassName();
   //获取原始的类 类型  
   Class clazz = ClassUtils.resolveClassName(className, null);
   ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
         ? (ConfigurableBeanFactory) registry : null;
   String contextId = getContextId(beanFactory, attributes);
   //获取client服务名  内部可基于beanFactory.resolveEmbeddedValue 解析 ${}占位符
   String name = getName(attributes);
   FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
   factoryBean.setBeanFactory(beanFactory);
   factoryBean.setName(name);
   factoryBean.setContextId(contextId);
   factoryBean.setType(clazz);
   //这里使用了Supplier的方式,beanDefinition如果绑定了Supplier,
      //在AbstractAutowireCapableBeanFactory#createBeanInstance创建bean时使用Supplier#get方法,
      //而不是factoryMethod或构造器
   BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
      //同上getName 内部可基于beanFactory.resolveEmbeddedValue 解析 ${}占位符
      factoryBean.setUrl(getUrl(beanFactory, attributes));
      factoryBean.setPath(getPath(beanFactory, attributes));
     //一般情况下如果对方接口抛异常404时,而又没有catch时,前端会收到500错误 ,decode404会将404会返回前端404错误信息
     factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
      Object fallback = attributes.get("fallback");
       //fallback配置
       ...
      return factoryBean.getObject();
   });
 //spring依赖处理篇有介绍相关AutowireMode,AbstractBeanDefinition.AUTOWIRE_BY_TYPE比AUTOWIRE_BY_NAME更严格
   //AUTOWIRE_BY_NAME找不到相关依赖不会报错
   definition.setAutowireMode(AbstractBeanDefAbstractBeanDefinition.AUTOWIRE_BY_TYPE比NAMEinition.AUTOWIRE_BY_TYPE);
   definition.setLazyInit(true);
   //校验fallback不能为接口 否则就不能回调了
   validate(attributes);

   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
   beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
   ...
   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
   //注册beanDefinition  
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

在历史版本中,上面registerBeanDefinition注册的是FeignClientFactoryBean,使用 definition.addPropertyValue("url", getUrl(attributes)property为FeignClientFactoryBean注入参数。 其实现过程是先实例化FeignClientFactoryBean,继而在其他类引用client时才调用FactoryBean.getObject方法来实例化真正的client.新版使用Supplier调整了实例化真正的client的流程 , 实例化FactoryBean的过程也会历经populate,initializeBean阶段,而这几个阶段对于这个场景的FeignClientFactoryBean没有意义。并且FactoryBean创建的bean 没有populate阶段 不会进行属性注入,只有initializeBean中的 BeanPostProcessor#postProcessAfterInitialization 阶段。调整为Supplier的方式使得client变得更像一个普通的bean,开发者可以直接在client使用@Autowired @Value @PostConstruct等注解了

接下来我们看看FeignClientFactoryBean是如何创建真正的client的

2.FeignClientFactoryBean创建client

FeignClientFactoryBean#getTarget

<T> T getTarget() {
  FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
        : applicationContext.getBean(FeignContext.class);
  //创建  Feign.Builder,并读取容器中配置,先使用默认配置为Builder赋值:连接、超时时间,
     //RequestInterceptor拦截器,Retryer等。
  //再尝试根据服务名查找独有配置。
  Feign.Builder builder = feign(context);
  //url一般只在本地调试阶段使用 就是指向固定的ip 端口,所以非调试阶段url一般为空
  if (!StringUtils.hasText(url)) {
     if (!name.startsWith("http")) {
        //拼接服务名  http://order-serice
        url = "http://" + name;
     }
     else {
        url = name;
     }
     //拼接根路径 http://order-serice/order
     url += cleanPath();
     //使用负载均衡的形式
     return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
  }
  //下面的就是指定ip 端口的固定请求了。有兴趣的可以自己看看
  .....
}

FeignClientFactoryBean#feign

protected Feign.Builder feign(FeignContext context) {
   Feign.Builder builder = get(context, Feign.Builder.class)
         // required values
         .logger(logger)
         .encoder(get(context, Encoder.class))
         .decoder(get(context, Decoder.class))
         //解析方法元信息
         .contract(get(context, Contract.class));
   //读取配置信息
   configureFeign(context, builder);
   //基于 FeignBuilderCustomizer 定制化配置  在这里你想干嘛就干嘛。。。
   applyBuildCustomizers(context, builder);

   return builder;
}

FeignClientFactoryBean#configureFeign

protected void configureFeign(FeignContext context, Feign.Builder builder) {
//feign.client这个里面配置的东西 
   FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
         : applicationContext.getBean(FeignClientProperties.class);
   if (properties != null && inheritParentContext) {
      if (properties.isDefaultToProperties()) {
      //一般走这里 先配置默认参数 再配置default里的 再配置服务名下的配置 每一级都会判空,然后覆盖
         configureUsingConfiguration(context, builder);
         configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
         configureUsingProperties(properties.getConfig().get(contextId), builder);
      }
      else {
         configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
         configureUsingProperties(properties.getConfig().get(contextId), builder);
         configureUsingConfiguration(context, builder);
      }
   }
   else {
      configureUsingConfiguration(context, builder);
   }
}

FeignBuilderCustomizer

//为每个不同的服务注册 FeignBuilderCustomizer可以这么用。
当然 上面的configureFeign中其他所有参数形如 Request.Options,ErrorDecoder
  //都可以通过注解引入一个配置类的形式做到配置隔离
@FeignClient(value = "order",configuration = OrderConfig.class)
public interface OrderClient extends InitializingBean {
....
}


public class OrderConfig {

    @Bean
    public FeignBuilderCustomizer feignBuilderCustomizer(){
        return builder -> {
            builder.decode404();
        };
    }
}

FeignClientProperties

@ConfigurationProperties("feign.client")
public class FeignClientProperties {
   private boolean defaultToProperties = true;
   private String defaultConfig = "default";
   private Map<String, FeignClientConfiguration> config = new HashMap<>();
   private boolean decodeSlash = true;
   
   public static class FeignClientConfiguration {
	private Logger.Level loggerLevel;
	private Integer connectTimeout;
	private Integer readTimeout;
	private Class<Retryer> retryer;
	private Class<ErrorDecoder> errorDecoder;
	private List<Class<RequestInterceptor>> requestInterceptors;
	private Map<String, Collection<String>> defaultRequestHeaders;
	private Map<String, Collection<String>> defaultQueryParameters;
	private Boolean decode404;
 }
}


feign:
  httpclient:
    enabled: true
  client:
    config:
      default:
        #建立连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
        connectTimeout: 10000
        #指建立连接后从服务端读取到可用资源所用的时间
      order:
        ConnectTimeOut: 30000
        retryer: feign.Retryer.Default

HardCodedTarget

public static class HardCodedTarget<T> implements Target<T> {
  //声明@FeignClient的接口
  private final Class<T> type;
  //服务名
  private final String name;
  // http://服务名/根路径
  private final String url;
}

FeignClientFactoryBean#loadBalance

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
   //根据环境返回实例  笔者这里返回 FeignBlockingLoadBalancerClient 
   Client client = getOptional(context, Client.class);
   if (client != null) {
      builder.client(client);
      //和 DefaultTargeter
      Targeter targeter = get(context, Targeter.class);
      //DefaultTargeter.target方法 继而调用 Feign.Builder.target方法,传入HardCodedTarget
      return targeter.target(this, builder, context, target);
   }
  ...
}

Feign.Builder#target

public <T> T target(Target<T> target) {
    return build().newInstance(target);
  }

public Feign build() {
    Client client = Capability.enrich(this.client, capabilities);
    Retryer retryer = Capability.enrich(this.retryer, capabilities);
    List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
        .map(ri -> Capability.enrich(ri, capabilities))
        .collect(Collectors.toList());
    Logger logger = Capability.enrich(this.logger, capabilities);
    Contract contract = Capability.enrich(this.contract, capabilities);
    Options options = Capability.enrich(this.options, capabilities);
    Encoder encoder = Capability.enrich(this.encoder, capabilities);
    Decoder decoder = Capability.enrich(this.decoder, capabilities);
     //InvocationHandlerFactory.Default
    InvocationHandlerFactory invocationHandlerFactory =
        Capability.enrich(this.invocationHandlerFactory, capabilities);
    QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
        new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
            logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    ParseHandlersByName handlersByName =
        new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
            errorDecoder, synchronousMethodHandlerFactory);
     //最终将各类配置组装成 ReflectiveFeign对象
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

feign.ReflectiveFeign#newInstance

//target是包含类信息 url 服务名的 HardCodedTarget
public <T> T newInstance(Target<T> target) {
  //解析方法元信息 为每个方法创建不同的MethodHandler  MethodHandler可以理解为方法的代理执行对象
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  //建立方法 与 MethodHandler 映射关系
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

  for (Method method : target.type().getMethods()) {
    if (method.getDeclaringClass() == Object.class) {
      continue;
    } else if (Util.isDefault(method)) {
      //default方法创建  DefaultMethodHandler,其invoke 方法就是直接调用原始default方法
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  //创建 ReflectiveFeign.FeignInvocationHandler  传入HardCodedTarget方法 与MethodHandler 映射关系
  InvocationHandler handler = factory.create(target, methodToHandler); 
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);

  for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    //默认方法handler绑定到代理对象
    defaultMethodHandler.bindTo(proxy);
  }
  return proxy;
}

至此feignClient代理对象创建完毕,代理对象的InvocationHandler 其内部持有方法与MethodHandler的映射关系,MehthodHandler,MethodHandler解析了方法的元信息,包含配置信息和参数类型,参数解析器等。

ParseHandlersByName#apply
 public Map<String, MethodHandler> apply(Target target) {
  //基于SpringMvcContract解析类中的方法 获取方法元信息MethodMetadata
    List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
    Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
    for (MethodMetadata md : metadata) {
      BuildTemplateByResolvingArgs buildTemplate;
      //formdata
      if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
        buildTemplate =
            new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
      } else if (md.bodyIndex() != null) {
        //json
        buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
      } else {
       //query
        buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
      }
      if (md.isIgnored()) {
         ..
      } else {
        //创建MethodHandler (SynchronousMethodHandler)
        result.put(md.configKey(),
            factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
      }
    }
    return result;
  }
}
SpringMvcContract#parseAndValidateMetadata
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
   //根据方法名 方法参数  作key
   processedMethods.put(Feign.configKey(targetType, method), method);
   //下方BaseContract#parseAndValidateMetadata
   MethodMetadata md = super.parseAndValidateMetadata(targetType, method);

   RequestMapping classAnnotation = findMergedAnnotation(targetType, RequestMapping.class);
   ...组合类合方法的 accept consume headers属性
   return md;
}
BaseContract#parseAndValidateMetadata
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
  final MethodMetadata data = new MethodMetadata();
  data.targetType(targetType);
  data.method(method);
  data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
  data.configKey(Feign.configKey(targetType, method));
   ...
  //提取类上的RequestMapping注解 value值 作为 data.template().uri()值
  processAnnotationOnClass(data, targetType);

  //提取方法上的RequestMapping注解属性 并存入到 data.template()中
  for (final Annotation methodAnnotation : method.getAnnotations()) {
    processAnnotationOnMethod(data, methodAnnotation, method);
  }
   ....
  final Class<?>[] parameterTypes = method.getParameterTypes();
  final Type[] genericParameterTypes = method.getGenericParameterTypes();

  final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  final int count = parameterAnnotations.length;
  for (int i = 0; i < count; i++) {
    boolean isHttpAnnotation = false;
    if (parameterAnnotations[i] != null) {
      //基于RequestPartParameterProcessor,RequestHeaderParameterProcessor,RequestParamParameterProcessor,
      PathVariableParameterProcessor等 判断isHttpAnnotation,同时标记
      //同时根据ConversionService判断是否支持类型转换,并标记位置index
      isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
    }

    if (isHttpAnnotation) {
     //基于BitSet  忽略下标
      data.ignoreParamater(i);
    }

    if (parameterTypes[i] == URI.class) {
      //服务名可在方法参数中动态传入 后续的调用时会将uri和方法requestmapping中路径拼接起来
      data.urlIndex(i);
    } else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
      if (data.isAlreadyProcessed(i)) {
         ...
      } else {
        ...
        data.bodyIndex(i);
        data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
      }
    }
  }
  ....
  return data;
}

3. ReflectiveFeign.FeignInvocationHandler#invoke feignClient调用过程

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  ..equals  hash toString 方法..
  } else if ("toString".equals(method.getName())) {
    return toString();
  }
  return dispatch.get(method).invoke(args);
}

SynchronousMethodHandler#invoke

public Object invoke(Object[] argv) throws Throwable {
    // 拷贝配置 解析参数 
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    //同URI参数 包含超时时间的Options参数也可在 方法中动态传入
    Options options = findOptions(argv);
    //默认不开启重启
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
         //feign.Retryer.Default的实现机制是尝试5次,之后就抛异常了
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          throw...
        }
        ...
        continue;
      }
    }
}
 

ReflectiveFeign.BuildTemplateByResolvingArgs#create

public RequestTemplate create(Object[] argv) {
  //拷贝
  RequestTemplate mutable = RequestTemplate.from(metadata.template());
  //HardCodedTarget
  mutable.feignTarget(target);
  if (metadata.urlIndex() != null) {
    int urlIndex = metadata.urlIndex();
    //如果方法中有uri参数(只能是http://服务名/xx/xx形式,可以只有http://服务名),
    //动态拼接方法上的requestmappin 路径值
    mutable.target(String.valueOf(argv[urlIndex]));
  }
  Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
  for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
    //key是下标 value是参数名称
    int i = entry.getKey();
    Object value = argv[entry.getKey()];
    if (value != null) { // Null values are skipped.
      if (indexToExpander.containsKey(i)) {
        //基于ConversionService参数类型转换
        value = expandElements(indexToExpander.get(i), value);
      }
      for (String name : entry.getValue()) {
        // 接口需要的真正 key value 
        varBuilder.put(name, value);
      }
    }
  }
  //body   PathVariable  header 处理
  RequestTemplate template = resolve(argv, mutable, varBuilder);
  //queryMap  headerMap处理 queryMap就是RequestParam声明在map上,headerMap同理,
  //这样做传参更灵活,但光看方法不能知道到底传递了什么参数,需要到调用方法处查看
  return template;
}

SynchronousMethodHandler#executeAndDecode

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    //调用RequestInterceptor拦截器
    Request request = targetRequest(template);
    Response response;
    long start = System.nanoTime();
    try {
       //基于服务名本地查找本地缓存的服务列表 默认使用RoundRobinLoadBalance挑选服务实例,获取真正的ip地址
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      //io异常 就抛出RetryableException 让外层有重试的机会
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    if (decoder != null)
      return decoder.decode(response, metadata.returnType());

    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    //内部会基于http响应码判断是否调用  feign.codec.ErrorDecoder#decode 处理异常,、
   //如果响应头包含Retry-After ,内部还会再抛出RetryableException
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);
     ....
  }
ErrorDecoder.Default#decode
public Exception decode(String methodKey, Response response) {
  FeignException exception = errorStatus(methodKey, response);
  //有无Retry-After响应头
  Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));
  if (retryAfter != null) {
    return new RetryableException(
        response.status(),
        exception.getMessage(),
        response.request().httpMethod(),
        exception,
        retryAfter,
        response.request());
  }
  return exception;
}
FeignException#errorStatus
private static FeignException errorStatus(int status,
                                          String message,
                                          Request request,
                                          byte[] body) {
  //根据响应码 抛出各类子异常
  // status >= 400 && status < 500                                         
  if (isClientError(status)) {
    return clientErrorStatus(status, message, request, body);
  }
  //status >= 500 && status <= 599;
  if (isServerError(status)) {
    return serverErrorStatus(status, message, request, body);
  }
  return new FeignException(status, message, request, body);
}

基于上面我们可以知道,FeignClient的重试机制就是判断调用的时候是否发生了IO异常或者异常了但返回Retry-After响应头,所以像404,502,503等常见错误是不会进行重试的,那么我们只需要重写ErrorDecoder#decode 方法,只要响应码在404,502,503抛出RetryableException就可以了

//实例化就变成全局的了,
//Retry同理,如果实例化一个Retryer类,则该类变为全局默认配置。建议Retry只用来查询数据,会对服务方产生数据变化的
//接口不建议使用。有可能对方处理超时了,而调用方触发了重试,导致产生多个数据
@Component
public class FeignErrorDecoder extends ErrorDecoder.Default {
    @Override
    public Exception decode(String methodKey, Response response) {
        if (response.status() == 502 || response.status() == 503 || response.status() == 404) {
           return new RetryableException(response.status(), String.format("%s retried 5 times",response.request().url()),
                    response.request().httpMethod(), null, response.request());
        }
        return super.decode(methodKey,response);
    }
}

4.总结

@EnableEurekaClients注解为我们import了FeignClientsRegistrar,该类在容器扫描注册bean时,会扫描classpath下配置的包路径,找到带@FeignClient的接口类,注册beanDefinition,而definition的实例化是由FeignClientFactoryBean辅助完成的。FeignClientFactoryBean.getTarget方法读取容器中包含超时时间,encode,decode等配置信息,解析方法元信息,创建与之对应的MethodHandler,用来注入参数和调用请求。将方法与MethodHandler的映射关系收集起来,注册一个代理对象,我们声明@FeignClient的接口类,其调用逻辑就是委托给代理对象,继而代理对象通过映射关系找到 MethodHandler 继续委托处理的。

Feign的源码中留下了很多可供扩展的地方,在服务级别的参数,如Retryer, ErrorDecoder可通过配置文件,注解引入配置类, 或者直接实例化 不同的FeignBuilderCustomizer来根据条件定制配置。 接口级别的参数可以通过传参URI、Request.Options来动态调整请求地址,超时时间等。 同时可以通过自定义 RequestInterceptor的方式拦截请求,添加自定义逻辑。