目录

Feign的说明

简单集成方式

调用方式

OpenFeign调用原理

OpenFeign 包扫描原理

注册 FeignClient 到 Spring 的原理

OpenFeign 动态代理原理

对SpringMVC注解的解析

OpenFeign 发送请求的原理

FeignClientFactoryBean做了哪些事?

ReflectiveFeign做了哪些事?

为OpenFeign增加简单负载均衡

Feign的说明

Feign的英文释义:假装,装作,佯装

Feign 作用是来简化我们发起远程调用的代码的,简化成像调用本地方法那样。

Feign 和 OpenFeign 有很多大同小异之处,不同的是 OpenFeign 支持 MVC 注解。

简单总结下 OpenFeign 能用来做什么:

OpenFeign 是声明式的 HTTP 客户端,让远程调用更简单。 提供了HTTP请求的模板(RestTemplate),编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息 可以整合负载均衡组件(Ribbon、LoadBalancer)和 服务熔断(Hystix、Sentinel),不需要显示调用Spring Cloud Feign 在 Netflix Feign的基础上扩展了对SpringMVC注解的支持

简单集成方式

Pom加入依赖

应用程序加入注解

配置接口

配置文件增加调用地址

使用远程接口

调用方式

整体调用时序图

OpenFeign调用原理

初始化过程

调用过程

关键对象:

FeignClientFactoryBean.getObject,用于对接Spring,关联@FeignClient注解

ReflectiveFeign.newInstance      生成代理对象

HardCodedTarget               存储接口信息以及FeignClient信息

Client接口                    远程调用执行工具

OpenFeign 包扫描原理

1) 声明Feign调用接口时,不用加@Component 注解,因为在Application那里已经加了EnableFeignClients。在Application上加了EnableFeignClients注解,会使用 Spring 框架的 Import 注解导入FeignClientsRegistrar 类,自动检查@FeignClient注解

2) FeignClientsRegistrar 负责 Feign 接口的加载。

由于FeignClientsRegistrar 实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,所以,可以由ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry主动调起

3) registerFeignClients 会对指定路径执行包扫描

通过getBasePackages方法,从metadata里获取EnableFeignClients注解里的包扫描参数,存入basePackages集合中,可能是多个。通过路径扫描,在basePackage路径下,找到所有的备选组件将所有备选组件存入candidateComponents集合

4) 只保留带有 @FeignClient 的接口。

5) 循环将所有@FeignClient注释的接口注册到Spring中

代码示例:

注册 FeignClient 到 Spring 的原理

还是在 registerFeignClients 方法中,当 FeignClient 扫描完后,就要为这些 FeignClient 接口生成一个动态代理对象。

registerFeignClient调用eagerlyRegisterFeignClientBeanDefinition方法注册接口

首先,生成一个基于FeignClientFactoryBean的Bean定义

根据类的名字我们可以知道这是一个工厂Bean,用来创建 FeignClient Bean 的。

我们最开始用的 @FeignClient,里面有个参数 "remoteSysService",这个是注解的属性,当 OpenFeign 框架去创建 FeignClient Bean 的时候,就会使用这些参数去生成 Bean。

RegisterFeignClientBeanDefinition的注册步骤如下:

1)创建一个class是FeignClientFactoryBean的beanDefinition 对象;

2)解析@FeignClient注解定义接口的各个属性,将各个属性设置到beanDefinition 中。如url、path、name、contextId(就是我们定义的远程服务名称)、fallbackFactory等;

3)然后将 beanDefinition 转换成一个 BeanDefinitionHolder,这个 holder 就是包含了 beanDefinition, alias, beanName 信息。

4)最后将这个 holder 注册到 Spring 容器中。

Registry就是DefaultListableBeanFactory,他将RemoteSystemService注册到容器中,

需要注意的是beanDefinition里存的class不是RemoteSystemService,而是FeignClientFactoryBean,他做为一个工厂Bean为后续调用提供Bean的创建。

至此,FeignClient注册的初始化已经完成。

小节:注册到Spring容器之后,服务要调用接口的时候,就可以直接用 FeignClient 的接口方法了

返回结果如果使用集成HashMap的方式,可以在不定义Provider的实体对象即可以使用。

那么这个服务以及方法是如何被调起的呢?

OpenFeign 动态代理原理

FeignClientFactoryBean实现了FactoryBean接口

当RemoteSystemService被创建时,会调用FeignClientFactoryBean来创建。

FeignClientFactoryBean.getTarget创建出Feign.Builder

Feign.Builder.build创建出SynchronousMethodHandler.Factory(用于创建反射方法)

Feign.Builder.build创建出Client.Default(使用Java原生HttpURLConnection调用),Client是用来做远程调用的连接对象,如果pom加载OkHttp,也可以被OkHttp实现。

获取org.springframework.cloud.openfeign.DefaultTargeter(Target接口只能找到一个实现类)Targeter是用来做代理的中间对象。

通过resolveTarget方法获取HardCodedTarget

HardCodedTarget存取实际的调用信息

获取Target实现类DefaultTargeter通过Feign.Builder创建出ReflectiveFeign,

这段有点复杂,实际调用链是:FeignClientFactoryBean中的targeter.target  -->  DefaultTargeter中的feign.target  -->  Feign.target  -->  Feign.build()创建ReflectiveFeign  -->  ReflectiveFeign.newInstance创建出实际代理类。

ReflectiveFeign通过newInstance方法创建代理getTenantByTenantId(java.lang.Long),

target作用是存储远程调用信息,实际是HardCodedTarget对象。

最终返回了一个对getTenantByTenantId(java.lang.Long)的代理

FeignClient代理模式,使用FeignInvocationHandler持有一个对FeignClient注解接口的代理(Proxy),将注解接口中的MVC方法解析出来(例如GetMapping注解的方法),将接口包含的所有MVC方法存放到一个HashMap(dispatch)中,为后续提供调用。

对SpringMVC注解的解析

ReflectiveFeign.newInstance中,对Map methodToHandler = targetToHandlersByName.apply(target, requestContext)的说明。通过使用OpenFeign.support.SpringMvcContract#parseAndValidateMetadata方法,可以从Controller方法的@GetMapping中解析出MethodMetadata,然后使用这个MethodMetadataton通过createMethodHandler方法来创建MethodHandler。

在ReflectiveFeign的ParseHandlersByName中的apply方法中做出解析。

MethodMetadata中的对@GetMapping注解内容的解析:

OpenFeign 发送请求的原理

ReflectiveFeign.invoke中,通过Method对象,获取到SynchronousMethodHandler对象

最终调用的是SynchronousMethodHandler的invoke方法。

然后在SynchronousMethodHandler中,executeAndDecode方法调用了Client的execute

本次的Client是由Feign.Builder默认创建出来的

通过convertAndSend调用Java原生的HttpURLConnection对象实现Http请求

java.net.HttpURLConnection是抽象类

sun.net.www.protocol.http.HttpURLConnection是实现类

另外,如果增加okhttp依赖的话,就会使用okhttp作为客户端,来代替Client.Default(创建原生HttpURLConnection)。

Okhttp会使用连接池的方式来管理连接

这里就不在使用Client.Default创建连接,而是使用OkHttpClient了

FeignClientFactoryBean做了哪些事?

通过getObject创建FeignClient客户端

获取FeignClientFactory工厂

创建Feign.Builder

根据Client接口的实现情况,创建Client接口对应的实现,默认选择Client.Default

根据Targeter接口的实现情况,创建Targeter接口对应的实现,默认选择DefaultTargeter

根据Target接口的实现情况,创建Target接口对应的实现,默认选择Target.HardCodedTarget

在有URL的情况下,创建Proxy代理

在没有配置URL的情况下,查找负载均衡的Feign代理

ReflectiveFeign做了哪些事?

对Feign接口进行代理

解析target(FeignClient注解的方法)对象,生成MethodMetadata

根据MethodMetadata生成MethodHandler集合

根据MethodHandler集合生成InvocationHandler

根据InvocationHandler生成Proxy对象

将MethodHandler集合绑定到Proxy

实现一个InvocationHandler接口默认的FeignInvocationHandler

在调用Feign接口时,承担invoke的入口,将MethodHandler调起

为OpenFeign增加简单负载均衡

初始化过程

FeignClientFactoryBean.getTarget

调用过程

FeignBlockingLoadBalancerClient.execute调用BlockingLoadBalancerClient.choose选择服务器

也可以结合Nacos创建功能更强大的负载均衡。

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: 

发表评论

返回顶部暗黑模式