Spring-cloud Feign 的理解

栏目: Java · 发布时间: 5年前

内容简介:feign的调用流程读取注解信息:feigh流程:

feign的调用流程

读取注解信息: EnableFeignClients --> FeignClientsRegistrar --> FeignClientFactoryBean

feigh流程: ReflectiveFeign --> Contract --> SynchronousMethodHandler

相关configuration: FeignClientsConfigurationFeignAutoConfigurationDefaultFeignLoadBalancedConfigurationFeignRibbonClientAutoConfiguration (Ribbon)

在FeignClientsRegistrar中:

@Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //注册feign配置信息
        registerDefaultConfiguration(metadata, registry);
        //注册feign client
        registerFeignClients(metadata, registry);
    }

    private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //准备注入FeignClientFactoryBean
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        ...
    }

查看FeignClientFactoryBean:

@Override
    public Object getObject() throws Exception {
        FeignContext context = applicationContext.getBean(FeignContext.class);
        //构建Feign.Builder
        Feign.Builder builder = feign(context);
        //如果注解没有指定URL
        if (!StringUtils.hasText(this.url)) {
            String url;
            if (!this.name.startsWith("http")) {
                url = "http://" + this.name;
            }
            else {
                url = this.name;
            }
            url += cleanPath();
            return loadBalance(builder, context, new HardCodedTarget<>(this.type,
                    this.name, url));
        }
        //如果指定了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) {
                // 因为指定了URL且classpath下有Ribbon,获取client的delegate(unwrap)
                // 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 targeter.target(this, builder, context, new HardCodedTarget<>(
                this.type, this.name, url));
    }
    
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {
        //获取Feign Client实例
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            //DefaultTargeter或者HystrixTargeter
            Targeter targeter = get(context, Targeter.class);
            //调用builder的target,其中就调用了Feign的newInstance
            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?");
    }

FeignClientsConfiguration 配置了 Feign.Builder ,prototype类型:

@Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }

Feign的Builder.build返回了一个 ReflectiveFeign :

public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);
      //ReflectiveFeign构造参数
      //ParseHandlersByName作用是通过传入的target返回代理接口下的方法的各种信息(MethodHandler)
      //Contract:解析接口的方法注解规则,生成MethodMetadata
      //Options:Request超时配置
      //Encoder:请求编码器
      //Decoder:返回解码器
      //ErrorDecoder:错误解码器
      //SynchronousMethodHandler.Factory是构建SynchronousMethodHandler的工厂
      //Client:代表真正执行HTTP的组件
      //Retryer:该组决定了在http请求失败时是否需要重试
      //RequestInterceptor:请求前的拦截器
      //Logger:记录日志组件,包含各个阶段记录日志的方法和留给用户自己实现的log方法
      //Logger.Level:日志级别
      //decode404:处理404的策略,返回空还是报错
      //synchronousMethodHandlerFactory通过所有的信息去包装一个synchronousMethodHandler,在调用invoke方法的时候执行HTTP
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }

在调用 Feign.Builder 的target的时候,调用了 ReflectiveFeign.newInstance :

/**
   * creates an api binding to the {@code target}. As this invokes reflection, care should be taken
   * to cache the result.
   */
  @SuppressWarnings("unchecked")
  @Override
  //接收Target参数(包含feign代理接口的类型class,名称,http URL)
  public <T> T newInstance(Target<T> target) {
    //首先通过**ParseHandlersByName**解析出接口中包含的方法,包装RequestTemplate,组装成<name, MethodHandler>
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    //接口default方法List
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    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)));
      }
    }
    //InvocationHandlerFactory.Default()返回了一个ReflectiveFeign.FeignInvocationHandler对象,通过传入的methodHandler map 调用目标对象的对应方法
    InvocationHandler handler = factory.create(target, methodToHandler);
    //生成JDK代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
    //绑定接口的默认方法到代理对象
    for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

生成Feign代理对象的基本流程图:

Spring-cloud Feign 的理解

当调用接口方法时,实际上就是调用代理对象invoke方法:

@Override
  public Object invoke(Object[] argv) throws Throwable {
    //工厂创建请求模版
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    //每次克隆一个新的Retryer
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        //这里调用实际的Feign client execute
        return executeAndDecode(template);
      } catch (RetryableException e) {
        //失败重试
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

DefaultFeignLoadBalancedConfiguration 里实例化了 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);
            //delegate这里是Client.Default实例,底层调用的是java.net原生网络访问
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            //executeWithLoadBalancer会根据ribbon的负载均衡算法构建url,这里不展开
            return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                    requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }

以上所述就是小编给大家介绍的《Spring-cloud Feign 的理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

参与的胜利

参与的胜利

亨利·詹金斯 / 高芳芳 / 浙江大学出版社 / 2017-9-30 / CNY 42.00

《参与的胜利:网络时代的参与文化》是一场学者之间的对话,三位学者(亨利·詹金斯、伊藤瑞子和丹娜·博伊德)虽然来自不同的代际、不同的学科背景,但他们在相同的参与文化项目中展开合作,并试图解决相似的问题。 希望《参与的胜利:网络时代的参与文化》能够进一步激发团体内部及团体之间的对话,这些团体包括教育者、政策制定者、学者、关注参与文化的公民、业内人士、粉丝及其他任何关心我们文化的未来的人。理想的参......一起来看看 《参与的胜利》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具