欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring-cloud Feign 的深入理解

程序员文章站 2024-02-20 22:17:23
feign的调用流程 读取注解信息:enablefeignclients-->feignclientsregistrar-->feignclientfacto...

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<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);
    }
  }

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