feign原理详解_vip视频解析是什么原理

2022-11-09 11:33:55 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

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 注解的作用。
  • 那么就需要看下接口的作用了,定义了一个接口,肯定是不能调用接口的方法的,需要有实现类,这时候就就会通过代理,反射生成一个代理类,最后执行最终生成的请求。
代码语言:javascript复制
/** * 定义一个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 注册类), 将扫描后的接口注册。
代码语言:javascript复制
//扫描我们定义的包路径(默认为@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,那么这里是注册的核心逻辑
  1. 将 @FeignClient 注解的接口解析接口和注解的配置信息,包装为 一个 FeignClientFactoryBean 工厂 bean ,注册进 spring 的 beanDefinationMap中。
  1. 看到这里发现只是简单的将 beanDefinition 信息注册进 beanMap,但是还没有真正实例化,那么到底是在哪里实例化的呢?由于这个 bean 是 FeignClientFactoryBean,那么肯定是这个 bean 工厂进行创建的,进入这个类。发现一个重载的 getObject 方法,很明显这个是创建 bean 的方法,肯定是有人依赖 FeignClientBean 也就是我们这里的 ProviderClient,当被自动注入时,就会去获取getBean(ProviderClient.class) 方法,接着就会调用 bean 工厂去创建 bean。下图就是 IOC 的实际运行情况。
  1. 接着就会调用 FeignClientFactoryBean#getObject() 中创建 bean ,进入 getObject(),然后调用了 getTarget(),从 spring 容器中获取了 FeignContext 对象,可以看一下 FeignContext 什么时候注入的,发现在 FeignAutoConfiguration#feignContext() 就进行注入了,也就是 springboot 加载时,就实例化好了。FeignContext 官方注释:给每一个 client 都具有设置一个 springApplicationContext的属性,方便每个 client 获取需要的 bean。简单来说就是每个client 都可以具备 springApplicationContext 的功能。
  2. 使用 FeignContext 构造 Feign.Builder构造器,看名字可以知道就是生成 feiclient 的构造器,将需要的参数进行封装,此时的 ProviderClient 接口的调用url的前缀为:http://nacos-component-provider
  1. 调用 loadBalance() 方法,构造了 HystrixTargeter 对象,然后调用 HystrixTargeter#target 方法,发现这里调用了 Feign#target, 这里就是通过反射将之前的 Feign.Builder 创建出来代理对象,但是还有 ProviderClient 中的方法的 springmvc 的 @RequestMapping 等注解没有解析。
  1. 执行 build()方法,主要是构建配置hystrix集成所需的组件 ReflectiveFeign 对象,然后执行构建代理 bean 的核心方法newInstance(target)。
代码语言:javascript复制
// 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 等属性准备。
  1. 使用 jdk动态代理,创建完成代理 bean,其中 InvocationHandler 就是最终的调用方,它包括了 ProviderClient 的方法处理器等信息。到这里就是给 ProviderClient 接口构建了个代理对象,然后使用代理对象进行服务的调用。为什么用 jdk动态代理呢?很简单呀,因为这是一个接口,jdk 生成代理对象则必须要实现一个接口。
  1. 当调用 ProviderClient#test 方法时,观察注入的 ProviderClient,就是我们上一步生成好的代理对象,然后调用 ReflectiveFeign#invoke() 实现对方法的调用。
  1. 根据调用的方法,使用 dispatch 调用对应的 methodHandler,dispatch 就是之前构造代理对象中的方法映射 map,然后调用 SynchronousMethodHandler#invoke() 方法,根据方法上的注解信息 value=“test/{id}”、参数id=1,创建 RequestTemplate 对象,其中 URI=test/1。
  1. 使用 LoadBalancerFeignClient执行 request 请求,此时请求已经是一个完整的请求,调用LoadBalancerFeignClient#execute 执行请求的替换和请求调用。
  1. 通过服务名构建一个 FeignLoadBalancer(其中就是一个ZoneAwareLoadBalancer)动态获取 nacos 上服务名对应的注册的所有服务,然后再进行服务的选取,例如轮询、随机等负载均衡策略,默认是轮询可用的服务列表。
  1. 将获取到的服务 server 和旧的uri,进行组装,获取一个最终请求的uri,然后进行请求调用。使用默认的 Client#execute 执行 http 请求调用,然后获取结果,将返回结果进行处理。这就是 feignClient 的调用流程。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/185874.html原文链接:https://javaforall.cn

0 人点赞