Feign远程调用原理分析

1,015 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

Feign介绍

Feign 微服务之间的调用方式,属于较轻量级的调用方式,可以通过注解来进行调用,请求模式也是基于模版化,传入参数封装成请求的模式。

远程调用主要是基于FeignClient注解作用在接口上,被标注的接口可以通过@Resource注入后使用,让Spring容器来管理。

@FeignClient注解属性如下: Name:指定调用服务的名称,通过服务名称发现服务。 Url:调用服务的地址。 Decode404:调用服务出现失败的时候如果该字段为true,会进行decoder解码,为false则抛出FeignException异常。 Configuration:定义feign的配置模式。 Fallback:定义feign接口请求失败的降级方法。 FallbackFactory:通过工厂类,来创建降级容错方法。 Path:定义当前FeignClient的统一前缀

具体请求步骤

9CBE88DC-A459-4F51-984A-C3DB3777A563.png

Feign帮助我们做了些什么

Feign算做什么一个RPC框架,我们常见的RPC框架主要有Feign和Dubbo,Feign基于Ribbon构建RestTemplete是一种基于Http协议的RPC实现方式,而Dubbo则是基于私有的Tcp协议。

正如上面对Feign基介绍,Feign帮我简化了一个Http请求,直接通过接口的形式去请求环境上的服务。

Feign的基本原理

首先让我们剖析源码来看下,@EnableFeignClients注解到底是如何实现服务于服务之间的远程调用的。

FeignClients初始化

BB585540-3E81-4167-9551-6B182C171863.png 首先我们看@EnableFeignClients注解会有5个属性定义.

Value:需要我们指定具体调用的服务名称。

basePackages:用于扫描注释组件的基本包。Value()是此属性的别名(与之互斥),使用basepackageclass()作为基于string的包名的类型安全替代方案。

basePackageClasses:basePackages()的类型安全替代方案,用于指定要扫描注释组件的包。主要是需要考虑包下每一个类,除了被属性引用,不会被其他属性引用。

defaultConfiguration:默认加载的一些客户端组件,例如feign.codec。解码器,feign.codec。编码器,feign.Contract。

Clients:如果不进行配置,则默认使用@FeignClient标注的的类的列表。

我们看注解import的类FeignClientsRegistrar是用来初始化FeignClient配置 ,里面的registerDefaultConfiguration方法是用来加载@EnableFeignClients注解里configuration配置文件,通过Map接收EnableFeignClients注解的属性参数。

3903D0DD-DDEA-46D2-A30A-A9EC92C07A5A.png

看到BeanDefinitionBuilder相信大家都不陌生,Spring通过BeanDefinition来构造Bean的配置信息。通过BeanDefinitionBuilder来构建FeignClientFactoryBean,里面的参数在上面Feign的属性已经介绍过了

E8C421EC-D001-4676-A526-FF616DC02044.png FeignClientFactoryBean里面是由Feign.Builder、logger、encoder、decoder和contract。 这几个组件组成,encoder/decoder为数据格式转化的协议,logger接收日志,Feign 默认的contract协议

DE573C3D-6267-4DCD-A14B-4C862D1DE15E.png 看到这里,我们仅仅是看到一个属性的初始化流程,还没有看到远程调用是如何进行的,我们接着看。

Ribbon构建请求

我们前面说Feign基于Ribbon构建RestTemplete,我们在spring-cloud-openfeign的Jar包下看到有Ribbon的包,DefaultFeignLoadBalancedConfiguration类的feignClient方法,来构建一个Client客户端,

22A46391-7FA7-4779-8120-28E8A460BEDC.png

接着我们看到有LoadBalancerFeignClient的execute方法,FeignLoadBalancer类有一个静态内部类,会构建一个request请求 432431C5-C90C-4077-9A87-593056219A1B.png FeignLoadBalancer继承了AbstractLoadBalancerAwareClient抽象类

EAF030CD-C892-4764-AB6B-52EA32AA05BC.png 底层AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法真正构建了一个http请求。

82A51F11-C641-4E17-B68E-26727CF6470C.png

看到这个方法相信大家对里面的参数都不陌生,有涉及到服务端的host、port,显然是为了拼接需要访问服务端的URI

DD3BAA2F-76CD-4EF5-AE03-27E1DCF0A42E.png

最后通过调IClient接口执行execute请求服务端,我们看到这个接口的实现有很多。

79951E13-D1C6-40EE-8A0D-C0D27EFD13DE.png

5948515F-C284-4527-A7E3-E020ECDF625D.png 我引入的是ribbon-httpclient:2.3.0,可以看到有两个请求类已经被禁止,原因无非就是效率太低(猜的…)

BD254FDF-8EB1-4C14-99E8-BF4FF106646A.png

目前使用比较多的是OkHttp以及效率更高的OkHttp3,OkHttp3该框架能够高效率的允许连接到同一个主机地址的所有请求,同时还能够缓存响应数据来减少重复的网络请求 以及数据流量的消耗… 这个后面有空会出个OkHttp3的文章。

总结

这里我们对Feign的调用原理通过追踪代码,做了一个简单的分析,其实Feign远不止这些,它还有很多强大的功能,以及提到的Ribbon,作为客户端的负载均衡也是拥有强大的功能,后面会经过学习以及对框架底层的深究继续通过写文章,帮助大家一起学习。如果有分析不对的地方,希望小伙伴们及时提出错误🙏