spring cloud feign


返回顶部

@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}
    class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        private ResourceLoader resourceLoader;
        private Environment environment;

        public FeignClientsRegistrar() {
        }

        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            this.registerDefaultConfiguration(metadata, registry);//入口1
            this.registerFeignClients(metadata, registry);//入口2
        }
    }
    private void registerDefaultConfiguration(AnnotationMetadata metadata,
                                              BeanDefinitionRegistry registry) {
        //将@EnableFeignClients的所有属性值导入map中
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
        //如果@EnableFeignClients配置了defaultConfiguration,则往下运行。如果没有,rantion
        //会使用默认的FeignConfiguration,一般我们这里不会设置这些配置,都是默认,但是如果设置了,
        //就会好比我们自己写了@Configuration的类一样的效果,这里是自动配置了
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            //如果原始类中有内部类,获取内部类名
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            //否则获取原始类名
            else {
                name = "default." + metadata.getClassName();
            }
            //注册@EnableFeignClients的Configuration配置
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }
    public void registerFeignClients(AnnotationMetadata metadata,
                                     BeanDefinitionRegistry registry) {
        //获取扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;
        //将@EnableFeignClients的所有自定义配置属性放入内存map中
        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //初始化一个@FeignClient的标签类型过滤器
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        //如果设置了@EnableFeignClients的自定义配置,获取clients配置项中的Class数组
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        //如果没有自定义配置
        if (clients == null || clients.length == 0) {
            //扫描器添加标签过滤类型
            scanner.addIncludeFilter(annotationTypeFilter);
            //获取默认配置扫描包集合
            basePackages = getBasePackages(metadata);
        }
        else { //如果有自定义配置
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            //遍历配置的每一个client Class
            for (Class<?> clazz : clients) {
                //添加每一个client Class的包
                basePackages.add(ClassUtils.getPackageName(clazz));
                //添加每一个client Class的类名(包含内部类名)
                clientClasses.add(clazz.getCanonicalName());
            }
            //初始化一个内部类过滤器
            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                @Override
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            //扫描器添加过滤设置
            scanner.addIncludeFilter(
                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }
        //遍历扫描包集合
        for (String basePackage : basePackages) {
            //用扫描器扫描包,获取bean集合
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            //遍历扫描到的bean集合
            for (BeanDefinition candidateComponent : candidateComponents) {
                //如果该bean为一个标签定义的bean
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    //获取该bean的元数据
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    //断言该bean为一个接口(Interface)
                    Assert.isTrue(annotationMetadata.isInterface(),
                            "@FeignClient can only be specified on an interface");
                    //获取@FeignClient的所有属性放入map中
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());
                    //获取@FeignClient配置的name(name或者value或者serviceId必须配置一个,并取其一)
                    String name = getClientName(attributes);
                    //注册单个@FeignClient的Configuration配置
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //注册@FeignClient定义的bean
                    registerFeignClient(registry, annotationMetadata, attributes);//入口
                }
            }
        }
    }
  1. 实例化Feign的配置类
  2. 扫描所有@FeignClient注解的接口中的方法,通过registerFeignClient方法进行注册

继续看下【通过registerFeignClient方法进行注册】

private void registerFeignClient(BeanDefinitionRegistry registry,
      AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
   //获取被@FeignClient标签注解的类名称,这里是接口名
   String className = annotationMetadata.getClassName();
   //设置bean建造器的bean工厂
   BeanDefinitionBuilder definition = BeanDefinitionBuilder
         .genericBeanDefinition(FeignClientFactoryBean.class);//入口
   //断言@FeignClient配置的fallback,fallbackFactory必须为接口
   validate(attributes);
   //建造器添加@FeignClient各配置属性
   definition.addPropertyValue("url", getUrl(attributes));
   definition.addPropertyValue("path", getPath(attributes));
   String name = getName(attributes);
   definition.addPropertyValue("name", name);
   definition.addPropertyValue("type", className);
   definition.addPropertyValue("decode404", attributes.get("decode404"));
   definition.addPropertyValue("fallback", attributes.get("fallback"));
   definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
   definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

   String alias = name + "FeignClient";
   //使用该建造器建造bean
   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   //获取主bean标识
   boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
   //设置该bean是否为主bean
   beanDefinition.setPrimary(primary);
   //通过@FeignClient设置的限定符属性获取别名
   String qualifier = getQualifier(attributes);
   if (StringUtils.hasText(qualifier)) {
      alias = qualifier;
   }
   //初始化一个bean的类名、别名解析器
   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
         new String[] { alias });
   //通过bean工厂和别名解析器,将该bean注册到spring容器中
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
  1. 先定义了bean的工厂类FeignClientFactoryBean,通过这个工厂的getObject获取bean
  2. 将bean注册到spring容器

看一下【通过这个工厂的getObject获取bean】

public Object getObject() throws Exception {
    //直接调用了getTarget()方法
   return getTarget();
}

/**
 * @param <T> the target type of the Feign client
 * @return a {@link Feign} client created with the specified data and the context information
 */
<T> T getTarget() {
   //这个FeignContext在FeignAutoConfiguration配置中已经声明了,所以可以直接用applicationContext获取bean
   FeignContext context = applicationContext.getBean(FeignContext.class);
    //配置feign 的decoder、encoder、retryer、contract、RequestInterceptor等
    //这些有默认配置,在FeignAutoConfiguration及FeignClientsConfiguration中有默认配置
   Feign.Builder builder = feign(context);

   if (!StringUtils.hasText(this.url)) {
      //如果@FeignClient注解上指定了url,其实除非本地调试,一般不建议指定URL
      String url;
      if (!this.name.startsWith("http")) {
         url = "http://" + this.name;
      }
      else {
         url = this.name;
      }
       //处理URL,没配置URL时,这里的URL形式为http://name+/path
      url += cleanPath();
       //使用负载均衡处理feign 请求
      return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
            this.name, url));
   }
    //配置了FeignClient的具体URL
   if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
      this.url = "http://" + this.url;
   }
   String url = this.url + cleanPath();
   Client client = getOptional(context, Client.class);
   if (client != null) {
      if (client instanceof LoadBalancerFeignClient) {
         // not load balancing because we have a url,
         // but ribbon is on the classpath, so unwrap
         client = ((LoadBalancerFeignClient)client).getDelegate();
      }
      builder.client(client);
   }
   Targeter targeter = get(context, Targeter.class);
   return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
         this.type, this.name, url));
}
  1. 获取FeignContext,通过FeignContext配置feign的decoder、encoder、retryer、contract、RequestInterceptor等
    • decoder:将http请求的response转换成对象
    • encoder:将http请求的对象转换成http request body
    • contract:校验Feign Client上的注解及value值是否合法
    • retryer:定义http请求如果失败了是否应该重试以及重试间隔、方式等等
    • RequestInterceptor:feign发起请求前的拦截器,可以全局定义basic auth、发起请求前自动添加header等等
  2. 如果@FeignClient注解上指定了url,其实除非本地调试,一般不建议指定URL,直接通过url请求
  3. 如果@FeignClient注解上未指定了url,通过loadBalance方法负载均衡的方式请求

【通过loadBalance方法负载均衡的方式请求】

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
      HardCodedTarget<T> target) {
    //默认client为LoadBalancerFeignClient,为啥?参见DefaultFeignLoadBalancedConfiguration
   Client client = getOptional(context, Client.class);
   if (client != null) {
      builder.client(client);
       //这个Targeter默认为DefaultTargeter,参见FeignAutoConfiguration
      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-netflix-ribbon?");
}
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
               Target.HardCodedTarget<T> target) {
   return feign.target(target);
}
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}
//ReflectiveFeign
public <T> T newInstance(Target<T> target) {
    //这个apply方法就是ReflectiveFeign中的apply方法,返回了每个方法的调用包装类SynchronousMethodHandler
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
//这个target.type()返回的就是声明@FeignClient注解所在的class
  for (Method method : target.type().getMethods()) {
    if (method.getDeclaringClass() == Object.class) {
      continue;
    } else if(Util.isDefault(method)) {
      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对象,这个对象的invoke方法其实就是调用了SynchronousMethodHandler.invoke方法
  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;
}
public Map<String, MethodHandler> apply(Target key) {
    //获取类上的方法的元数据,如返回值类型,参数类型,注解数据等等
  List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
  Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
  for (MethodMetadata md : metadata) {
    BuildTemplateByResolvingArgs buildTemplate;
    if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
      buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
    } else if (md.bodyIndex() != null) {
      buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
    } else {
      buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
    }
      //这个factory是SynchronousMethodHandler.Factory,create方法返回了一个SynchronousMethodHandler对象
    result.put(md.configKey(),
               factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
  }
  return result;
}
  1. 注入的其实是Targeter类,Targetr类包装了FeignCLient的proxy,proxy内部绑定了methodHandler为SynchronousMethodHandler

执行过程

因为是通过jdk的动态代理类执行,所以会执行到自定义拦截器FeignInvocationHandler的invoke方法

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();
  }
  //除了equals、hashCode、toString方法外,其他方法都走dispatch.get(method).invoke(args)方法。
  //点击这个方法的实现类,就可以追到  SynchronousMethodHandler的invoke方法了。
  return dispatch.get(method).invoke(args);
}
public Object invoke(Object[] argv) throws Throwable {
  //根据调用参数创建一个RequestTemplate,用来具体处理http调用请求
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  //克隆出一个一模一样的Retryer,用来处理调用失败后的重试
  Retryer retryer = this.retryer.clone();
  while (true) {
    try {
      //发送http request以及处理response等  
      return executeAndDecode(template);
    } catch (RetryableException e) {
      //处理重试次数、重试间隔等等  
      retryer.continueOrPropagate(e);
      continue;
    }
  }
}
  1. 通过create创建RequestTemplate
  2. 调用executeAndDecode

【通过create创建RequestTemplate】

//ReflectiveFeign.BuildTemplateByResolvingArgs
public RequestTemplate create(Object[] argv) {
  //获取methodMetada的template,这个RequestTemplate是可变的,跟随每次调用参数而变。
  RequestTemplate mutable = new RequestTemplate(metadata.template());
  if (metadata.urlIndex() != null) {
    //处理@PathVariable在URL上插入的参数  
    int urlIndex = metadata.urlIndex();
    checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
    mutable.insert(0, String.valueOf(argv[urlIndex]));
  }
  //处理调用方法的param参数,追加到URL ?后面的参数
  Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
  for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
    int i = entry.getKey();
    Object value = argv[entry.getKey()];
    if (value != null) { // Null values are skipped.
      if (indexToExpander.containsKey(i)) {
        value = expandElements(indexToExpander.get(i), value);
      }
      for (String name : entry.getValue()) {
        varBuilder.put(name, value);
      }
    }
  }
  //处理query参数以及body内容   
  RequestTemplate template = resolve(argv, mutable, varBuilder);
  if (metadata.queryMapIndex() != null) {
    // add query map parameters after initial resolve so that they take
    // precedence over any predefined values
    //当  RequestTemplate处理完参数后,再处理@QueryMap注入的参数,以便优先于任意值。
    Object value = argv[metadata.queryMapIndex()];
    Map<String, Object> queryMap = toQueryMap(value);
    template = addQueryMapQueryParameters(queryMap, template);
  }

  if (metadata.headerMapIndex() != null) {
    //处理RequestTemplate的header内容  
    template = addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
  }

  return template;
}

根据调用时的参数等构造了RequestTemplate的param、body、header等内容。

【调用executeAndDecode】

//SynchronousMethodHandler
Object executeAndDecode(RequestTemplate template) throws Throwable {
  //构造Request,将RequestTemplate中的参数等放入Request中
  Request request = targetRequest(template);
  Response response;
  try {
    //这个client默认实现是Client接口中的Defalut,实现是通过HttpURLConnection发送请求
    //另一种是LoadBalancerFeignClient,默认也是Client接口中的Defalut,可以通过配置指定为Apache的HTTPClient,也可以指定为OKhttp来发送请求,在每个具体实现中来通过ribbon实现负载均衡,负载到集群中不同的机器,这里不再发散  
    response = client.execute(request, options);//入口
    // ensure the request is set. TODO: remove in Feign 10
    response.toBuilder().request(request).build();
  } catch (IOException e) {
    throw errorExecuting(request, e);
  }
  boolean shouldClose = true;
  try {
    //处理response的返回值
    if (Response.class == metadata.returnType()) {
      if (response.body() == null) {
        return response;
      }
      // Ensure the response body is disconnected
      byte[] bodyData = Util.toByteArray(response.body().asInputStream());
      return response.toBuilder().body(bodyData).build();
    }
    //根据状态码处理下response
    if (response.status() >= 200 && response.status() < 300) {
      if (void.class == metadata.returnType()) {
        return null;
      } else {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      }
    } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
      Object result = decode(response);
      shouldClose = closeAfterDecode;
      return result;
    } else {
      throw errorDecoder.decode(metadata.configKey(), response);
    }
  } 
}
//LoadBalancerFeignClient
@Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                    requestConfig).toResponse();//入口
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }
//AbstractLoadBalancerAwareClient
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
        LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri())
                .build();

        try {
            return command.submit(//入口
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }

    }
    public Observable<T> submit(final ServerOperation<T> operation) {
        //入口
        //创建Observable
        //selectServer方法选择server
        Observable<T> o = (this.server == null ? this.selectServer() : Observable.just(this.server)).concatMap(new Func1<Server, Observable<T>>() {
            public Observable<T> call(Server server) {
                context.setServer(server);
                final ServerStats stats = LoadBalancerCommand.this.loadBalancerContext.getServerStats(server);
                Observable<T> o = Observable.just(server).concatMap(new Func1<Server, Observable<T>>() {
                    public Observable<T> call(final Server server) {
                        //内部代码省略
                    }
                });
                if (maxRetrysSame > 0) {
                    o = o.retry(LoadBalancerCommand.this.retryPolicy(maxRetrysSame, true));
                }

                return o;
            }
        });
        if (maxRetrysNext > 0 && this.server == null) {
            o = o.retry(this.retryPolicy(maxRetrysNext, false));
        }

        return;
    }
private Observable<Server> selectServer() {
        return Observable.create(new OnSubscribe<Server>() {
            @Override
            public void call(Subscriber<? super Server> next) {
                try {
                    Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                    next.onNext(server);
                    next.onCompleted();
                } catch (Exception e) {
                    next.onError(e);
                }
            }
        });
    }
posted @ 2020-04-07 19:29  平淡454  阅读(182)  评论(0编辑  收藏  举报