zoukankan      html  css  js  c++  java
  • 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);
                    }
                }
            });
        }
    
  • 相关阅读:
    Sum of a Function(区间筛)
    (01背包)输出方案数
    删边求概率
    完全背包输出方案数(dp)
    二分
    Just Arrange the Icons(模拟)
    Balls of Buma(回文串)
    dp思想
    刷题-力扣-190. 颠倒二进制位
    刷题-力扣-173. 二叉搜索树迭代器
  • 原文地址:https://www.cnblogs.com/yanhui007/p/12655482.html
Copyright © 2011-2022 走看看