栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-cloud Feign 的深入理解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring-cloud Feign 的深入理解

feign的调用流程

读取注解信息:EnableFeignClients-->FeignClientsRegistrar-->FeignClientFactoryBean
feigh流程:ReflectiveFeign-->Contract-->SynchronousMethodHandler
相关configuration:FeignClientsConfiguration,FeignAutoConfiguration,DefaultFeignLoadBalancedConfiguration,FeignRibbonClientAutoConfiguration(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 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 loadBalance(Feign.Builder builder, FeignContext context,
      HardCodedTarget 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:

 
 @SuppressWarnings("unchecked")
 @Override
 //接收Target参数(包含feign代理接口的类型class,名称,http URL)
 public  T newInstance(Target target) {
  //首先通过**ParseHandlersByName**解析出接口中包含的方法,包装RequestTemplate,组装成
  Map nameToHandler = targetToHandlersByName.apply(target);
  Map methodToHandler = new linkedHashMap();
  //接口default方法List
  List defaultMethodHandlers = new linkedList();

  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代理对象的基本流程图:

当调用接口方法时,实际上就是调用代理对象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);
    }
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/139003.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号