还不懂Feign工作机制?

1,990 阅读5分钟

src=http___pic2.zhimg.com_v2-8764ab2f75424a591289ec94d1082cb3_1440w.jpg_source=172ae18b&refer=http___pic2.zhimg.webp

前言

微服务调用中我们最常用的就是使用feign,使用feign往往离不开@EnableFeignClients和@FeignClient, 在如此简单的使用下,我们或多或少的要去关心一下他的底层实现。

Feign的注册

@EnableFeignClients

6F98nqNUEb.jpg @EnableFeignClients中导入了FeignClientsRegistrar

T7mX5VxtBp.jpg 这里实现了ImportBeanDefinitionRegistrar并重写了Bean注册方法

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    //注册默认的配置
    this.registerDefaultConfiguration(metadata, registry);
    //注册FeignClients
    this.registerFeignClients(metadata, registry);
}

这里注册了两类东西,我们一个一个来去看

注册默认配置

private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    //获取注解上的属性
    Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
        //这里的逻辑就是判断一下是不是内部类,然后组装name
        String name;
        if (metadata.hasEnclosingClass()) {
            name = "default." + metadata.getEnclosingClassName();
        } else {
            name = "default." + metadata.getClassName();
        }

        this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
    }

}
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
    //创建一个FeignClientSpecification的Bean定义并且设置构造方法
   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
   builder.addConstructorArgValue(name);
   builder.addConstructorArgValue(configuration);
   //注册
   registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
         builder.getBeanDefinition());
}

这里可以看到注册的配置信息是从EnableFeignClients中的defaultConfiguration取出的,一般用作自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。

注册FeignClients

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

   LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
   //获取EnableFeignClients注解上的属性信息
   Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
   final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
   // 如果clients属性为null,则获取basePackages属性,扫描其中的所有client
   if (clients == null || clients.length == 0) {
       //获取扫描器
      ClassPathScanningCandidateComponentProvider scanner = getScanner();
      scanner.setResourceLoader(this.resourceLoader);
      //扫描带有FeignClient注解的类
      scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
      //获取@FeignClients注解中的扫描包路径
      Set<String> basePackages = getBasePackages(metadata);
      for (String basePackage : basePackages) {
         //扫描获取Beand定义
         candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
      }
   }
   else {
       //如果clients属性不为null,则直接注入
      for (Class<?> clazz : clients) {
         candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
      }
   }

   for (BeanDefinition candidateComponent : candidateComponents) {
      if (candidateComponent instanceof AnnotatedBeanDefinition) {
         // verify annotated class is an interface
         AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
         //获取注解元数据
         AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
         //断言是不是作用在接口上
         Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
         //获取@FeignClient的属性信息
         Map<String, Object> attributes = annotationMetadata
               .getAnnotationAttributes(FeignClient.class.getCanonicalName());

         String name = getClientName(attributes);
         //这和上面注册默认配置一样
         registerClientConfiguration(registry, name, attributes.get("configuration"));
         //核心方法 真正的开始注册加了@FeignClients注解的类
         registerFeignClient(registry, annotationMetadata, attributes);
      }
   }
}

上面的逻辑是在扫描@FeignClients获取Bean定义,然后注册一下@FeignClients中属性configuration的配置类。
真正的注册加了@FeignClients类的核心代码在下面,他的传参并不是直接传的刚刚扫描的Bean定义,而是Bean定义的元数据。这么做是因为他要构建一个新的Bean定义而不是去使用直接扫描出来的,具体看下面源码。

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
      Map<String, Object> attributes) {
   //类全路径名称
   String className = annotationMetadata.getClassName();
   //加载类
   Class clazz = ClassUtils.resolveClassName(className, null);
   //获取Bean工厂
   ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
         ? (ConfigurableBeanFactory) registry : null;
   String contextId = getContextId(beanFactory, attributes);
   String name = getName(attributes);
   //这里其实是把加了刚刚加载的类封装成FeignClientFactoryBean
   FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
   factoryBean.setBeanFactory(beanFactory);
   factoryBean.setName(name);
   factoryBean.setContextId(contextId);
   factoryBean.setType(clazz);
   factoryBean.setRefreshableClient(isClientRefreshEnabled());
   BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
      factoryBean.setUrl(getUrl(beanFactory, attributes));
      factoryBean.setPath(getPath(beanFactory, attributes));
      factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
      Object fallback = attributes.get("fallback");
      if (fallback != null) {
         factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
               : ClassUtils.resolveClassName(fallback.toString(), null));
      }
      Object fallbackFactory = attributes.get("fallbackFactory");
      if (fallbackFactory != null) {
         factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
               : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
      }
      return factoryBean.getObject();
   });
   //下面就是设置一些属性然后注册Bean了
   definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
   definition.setLazyInit(true);
   validate(attributes);

   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
   beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

   // has a default, won't be null
   boolean primary = (Boolean) attributes.get("primary");

   beanDefinition.setPrimary(primary);

   String[] qualifiers = getQualifiers(attributes);
   if (ObjectUtils.isEmpty(qualifiers)) {
      qualifiers = new String[] { contextId + "FeignClient" };
   }

   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

   registerOptionsBeanDefinition(registry, contextId);
}

流程图解析

Feign自动装载.png

生成代理对象

我们着重要看一下这段逻辑,FeignClientFactoryBean实现了FactoryBean

BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
   factoryBean.setUrl(getUrl(beanFactory, attributes));
   factoryBean.setPath(getPath(beanFactory, attributes));
   factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
   Object fallback = attributes.get("fallback");
   if (fallback != null) {
      factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
            : ClassUtils.resolveClassName(fallback.toString(), null));
   }
   Object fallbackFactory = attributes.get("fallbackFactory");
   if (fallbackFactory != null) {
      factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
            : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
   }
   //最终生成的Bean定义是这里获取的返回值
   return factoryBean.getObject();
});
@Override
public Object getObject() {
   return getTarget();
}
<T> T getTarget() {
    // 获取Feign的上下文
   FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
         : applicationContext.getBean(FeignContext.class);
   Feign.Builder builder = feign(context);

   if (!StringUtils.hasText(url)) {

      if (LOG.isInfoEnabled()) {
         LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
      }
      //如果url没有定义,则进入到该判断中创建对象,该判断中创建的对象具有负载均衡功能
      if (!name.startsWith("http")) {
         url = "http://" + name;
      }
      else {
         url = name;
      }
      url += cleanPath();
      return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
   }
   if (StringUtils.hasText(url) && !url.startsWith("http")) {
      url = "http://" + url;
   }
   //如果url定义了,就没有必要进行负载均衡了
   String url = this.url + cleanPath();
   Client client = getOptional(context, Client.class);
   if (client != null) {
      if (client instanceof FeignBlockingLoadBalancerClient) {
         // not load balancing because we have a url,
         // but Spring Cloud LoadBalancer is on the classpath, so unwrap
         client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
      }
      if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
         // not load balancing because we have a url,
         // but Spring Cloud LoadBalancer is on the classpath, so unwrap
         client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
      }
      builder.client(client);
   }
   Targeter targeter = get(context, Targeter.class);
   return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}

创建负载均衡的代理对象

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
   Client client = getOptional(context, Client.class);
   if (client != null) {
      builder.client(client);
      Targeter targeter = get(context, Targeter.class);
      return targeter.target(this, builder, context, target);
   }

   throw new IllegalStateException(
         "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}

这里的主要逻辑:

  • 如果没有指定url则创建负载均衡的代理对象。先是拿出client,然后获取client的代理对象。我们最终使用的FeignClient是Client的动态代理对象,而Client对象是真正执行http请求的对象
  • 如果没有指定url则不必要进行负载均衡,这里是做了两个if判断,client属于FeignBlockingLoadBalancerClient或者RetryableFeignBlockingLoadBalancerClient会代理他们中的delegate。
  • 最终都要去执行targeter.target()方法

调用链Targeter.target()→DefaultTargeter.target()→Feign.Builder.target()→Feign.newInstance()→ReflectiveFeign.newInstance()

public <T> T newInstance(Target<T> target) {
  //获取类中用户创建的方法
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  //遍历类中方法
  for (Method method : target.type().getMethods()) {
      //Obbject的方法
    if (method.getDeclaringClass() == Object.class) {
      continue;
    } else if (Util.isDefault(method)) {
      //default方法创建DefaultMethodHandler处理
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      //这一步method就都是用户创建的,从nameToHandler取出
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  //动态代理逻辑
  InvocationHandler handler = factory.create(target, methodToHandler);
  //创建动态代理对象
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);

  for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    defaultMethodHandler.bindTo(proxy);
  }
  return proxy;
}

Feign的调用

刚刚只是讲了加了@FeignClients注解的类如何生成代理对象并注册到容器中,接下来就要看在使用的时候他是如何帮我们去进行的远程调用。
既然是代理对象就会调用代理逻辑,我们看刚刚创建动态代理逻辑的方法
调用链路:factory.create -> Default#create -> FeignInvocationHandler
我们看到他实现了InvocationHandler,代理逻辑就会走invoke方法

static class FeignInvocationHandler implements InvocationHandler {

  private final Target target;
  //存放着刚刚的methodToHandler
  private final Map<Method, MethodHandler> dispatch;

  FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
    this.target = checkNotNull(target, "target");
    this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
  }

  @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();
    }
    //这是我们主要看的
    return dispatch.get(method).invoke(args);
  }

}
public Object invoke(Object[] argv) throws Throwable {
  // 解析请求参数,封装 RequestTemplate\
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  // 获取 Request.Options 对象,封装了请求的 connectTimeout/readTimeout 等信息
  Options options = findOptions(argv);
  // 重试机制
  Retryer retryer = this.retryer.clone();
  while (true) {
    try {
      //远程调用逻辑
      return executeAndDecode(template, options);
    } 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;
    }
  }
}