IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Feign 原理解析 -> 正文阅读

[Java知识库]Feign 原理解析

Feign 原理解析

基本原理

现在已经了解了 Ribbon 的负载均衡原理,我们可以来猜想下,Feign 的原理,仅仅通过一个注解 @FeignClient + 一个接口,就可以服务之间的调用。

  • 通过 @FeignClient 在注解中的name,确定服务名,然后 RibbonClient 使用服务名去获取负载均衡器 loadBalancer,再通过负载均衡算法获取到 ip:port, 然后构建的请求为 http://nacos-component-provider/test/{id},当 id = 1时,最后通过参数封装等,请求为http://nacos-component-provider/test/1,然后进行服务名替换,http://ip:port/test/1。这个就是最终的请求,也是 @FeignClient 注解的作用。
  • 那么就需要看下接口的作用了,定义了一个接口,肯定是不能调用接口的方法的,需要有实现类,这时候就就会通过代理,反射生成一个代理类,最后执行最终生成的请求。
/**
 * 定义一个REST风格的请求接口,如果 ribbon 可用,它将具备负载均衡的功能。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
    // 一般为服务名
	@AliasFor("name")
	String value() default "";
}

//例子,构造的请求最终会是: http://nacos-component-provider/test/{id}
@FeignClient(value = "nacos-component-provider")
public interface ProviderClient {
    @GetMapping("test/{id}")
    String test(@PathVariable("id") Integer id);
}

Feign 初始化

要想使用 Feign,就首先需要加上 @EnableFeignClients 注解开启 Feign功能,然后创建接口和调用方法,接着加上注解 @FeignClient 就可以进行使用了。

  1. 首先 @EnableFeignClients 注解是怎么开启 Feign 功能的,可以看到注解上 @Import FeignClientsRegistrar (feign client 注册类), 将扫描后的接口注册。
//扫描我们定义的包路径(默认为@SpringBootApplication类所在的包)下带有 @FeignClient 注解的接口,然后将这些接口注册为 feign  clients 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

    //扫描的包路径
	String[] value() default {};

	// 扫描的包路径
	String[] basePackages() default {};
}
  1. 进入 FeignClientsRegistrar,查看 registerBeanDefinitions ,这里是使用了 @EnableFeignClients,那么这里是注册的核心逻辑

  2. 将 @FeignClient 注解的接口解析接口和注解的配置信息,包装为 一个 FeignClientFactoryBean 工厂 bean ,注册进 spring 的 beanDefinationMap中。

  3. 看到这里发现只是简单的将 beanDefinition 信息注册进 beanMap,但是还没有真正实例化,那么到底是在哪里实例化的呢?由于这个 bean 是 FeignClientFactoryBean,那么肯定是这个 bean 工厂进行创建的,进入这个类。发现一个重载的 getObject 方法,很明显这个是创建 bean 的方法,肯定是有人依赖 FeignClientBean 也就是我们这里的 ProviderClient,当被自动注入时,就会去获取getBean(ProviderClient.class) 方法,接着就会调用 bean 工厂去创建 bean。下图就是 IOC 的实际运行情况。

  4. 接着就会调用 FeignClientFactoryBean#getObject() 中创建 bean ,进入 getObject(),然后调用了 getTarget(),从 spring 容器中获取了 FeignContext 对象,可以看一下 FeignContext 什么时候注入的,发现在 FeignAutoConfiguration#feignContext() 就进行注入了,也就是 springboot 加载时,就实例化好了。FeignContext 官方注释:给每一个 client 都具有设置一个 springApplicationContext的属性,方便每个 client 获取需要的 bean。简单来说就是每个client 都可以具备 springApplicationContext 的功能。

  5. 使用 FeignContext 构造 Feign.Builder构造器,看名字可以知道就是生成 feiclient 的构造器,将需要的参数进行封装,此时的 ProviderClient 接口的调用url的前缀为:http://nacos-component-provider

  6. 调用 loadBalance() 方法,构造了 HystrixTargeter 对象,然后调用 HystrixTargeter#target 方法,发现这里调用了 Feign#target, 这里就是通过反射将之前的 Feign.Builder 创建出来代理对象,但是还有 ProviderClient 中的方法的 springmvc 的 @RequestMapping 等注解没有解析。

  7. 执行 build()方法,主要是构建配置hystrix集成所需的组件 ReflectiveFeign 对象,然后执行构建代理 bean 的核心方法newInstance(target)。

// build() 构造配置hystrix集成所需的组件 ReflectiveFeign,然后再实例化最终的 bean 对象
public <T> T target(Target<T> target) {
	return build().newInstance(target);
}
  1. 首先通过反射获取 ProviderClient 类中的所有方法,构建为 ProviderClient#test() 为key,value为对应的 methodHandler 形式的 map,其中 methodHandler 具有一个负载均衡客户端 LoadBalancerFeignClient ,用于后面使用 LoadBalancerFeignClient 调用方法,这里是为了后面获取 method上注解的属性,例如 url,httpMethod 等属性准备。

  2. 使用 jdk动态代理,创建完成代理 bean,其中 InvocationHandler 就是最终的调用方,它包括了 ProviderClient 的方法处理器等信息。到这里就是给 ProviderClient 接口构建了个代理对象,然后使用代理对象进行服务的调用。为什么用 jdk动态代理呢?很简单呀,因为这是一个接口,jdk 生成代理对象则必须要实现一个接口。

  3. 当调用 ProviderClient#test 方法时,观察注入的 ProviderClient,就是我们上一步生成好的代理对象,然后调用 ReflectiveFeign#invoke() 实现对方法的调用。

  4. 根据调用的方法,使用 dispatch 调用对应的 methodHandler,dispatch 就是之前构造代理对象中的方法映射 map,然后调用 SynchronousMethodHandler#invoke() 方法,根据方法上的注解信息 value=“test/{id}”、参数id=1,创建 RequestTemplate 对象,其中 URI=test/1。

  5. 使用 LoadBalancerFeignClient执行 request 请求,此时请求已经是一个完整的请求,调用LoadBalancerFeignClient#execute 执行请求的替换和请求调用。

  6. 通过服务名构建一个 FeignLoadBalancer(其中就是一个ZoneAwareLoadBalancer)动态获取 nacos 上服务名对应的注册的所有服务,然后再进行服务的选取,例如轮询、随机等负载均衡策略,默认是轮询可用的服务列表。

  7. 将获取到的服务 server 和旧的uri,进行组装,获取一个最终请求的uri,然后进行请求调用。使用默认的 Client#execute 执行 http 请求调用,然后获取结果,将返回结果进行处理。这就是 feignClient 的调用流程。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-27 12:42:26  更:2021-10-27 12:44:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:11:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码