好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Spring-cloud Feign 的深入理解

feign的调用流程

读取注解信息:enablefeignclients-->feignclientsregistrar-->feignclientfactorybean
feigh流程:reflectivefeign-->contract-->synchronousmethodhandler
相关configuration:feignclientsconfiguration,feignautoconfiguration,defaultfeignloadbalancedconfiguration,feignribbonclientautoconfiguration(ribbon)

在feignclientsregistrar中:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

@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类型:

?

1

2

3

4

5

6

@bean

@scope ( "prototype" )

@conditionalonmissingbean

public feign.builder feignbuilder(retryer retryer) {

   return feign.builder().retryer(retryer);

}

feign的builder.build返回了一个reflectivefeign:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

/**

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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

   }

}

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

原文链接:https://segmentfault测试数据/a/1190000018077675

查看更多关于Spring-cloud Feign 的深入理解的详细内容...

  阅读:9次