一、什么是Feign?
Feign makes writing java http clients easier
,这是官方给出的一个说明,本意翻译是:Feign使编写Java http客户端更容易,Feign
是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,Feign可以通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求。
Feign封装了Http调用流程,更适合面向接口化的编程习惯。
在服务调用的场景中,我们经常调用基于Http协议的服务,而我们经常使用到的远程调用框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Forest、Netty等等,这些框架在基于自身的专注点提供了自身特性。而从角色划分上来看,他们的职能是一致的提供Http调用服务。
二、理解远程调用
- 本地调用(Local Procedure Call,简称
LPC
) - 远程调用(Remote Procedure Call,简称
RPC
)
feign主要是为我们提供了远程调用的服务,那么什么是远程调用呢? 远程调用说白了可以理解为不同服务之间方法的调用,实质上是两台主机间的网络通信 ,涉及到网络通信又必然会有序列化、反序列化,编解码等一些必须要考虑的问题,现在业界内比较流行的一些 RPC 框架,例如:Dubbo
提供的是基于接口的远程方法调用,通过rpc远程调用框架,客户端只需要知道接口的定义即可调用远程服务。
而 feign主要就是用来简化我们发起远程调用的代码,以一个远程调用Github开放的API为:
/** * GitHub客户端GitHubFeign,访问GitHub开发平台API,开放平台API地址:https://www.apifox.cn/apihub/ * * @author: jacklin * @date: 2022/6/30 21:20 */ @FeignClient(name = "github-client", url = "https://api.github.com") public interface GitHubFeign { /** * 查找github标准库信息 ** https://api.github.com/search/repositories v * * @author: jacklin * @since 2022/6/30 21:27 **/ @GetMapping(value = "/search/repositories", produces = MediaType.APPLICATION_JSON_VALUE) String searchRepositories(@RequestParam("q") String q); }
第一步: Maven pom文件中引入 OpenFeign 组件。
第二步:客户端需要定义一个GitHubFeign
接口,里面定义一个searchRepositories()
方法,可以看到这个接口上添加了@FeignClient
注解,而括号里面指定了服务名:github-client
,显示声明这个接口是用来远程调用GitHub API
服务的,url用来指定调用服务的全路径,其他方法路径前缀必须与url地址一致,完整的请求路径URL地址:https://api.github.com/search/repositories
第三步:需要在服务启动类添加@EnableFeignClients
注解,在服务启动时,Spring扫描被@FeignClints
修饰的接口,基于动态代理生成本地JDK Proxy代理对象实例,然后将这些代理实例注册到Spring IOC容器中,当远程接口被调用时,由Proxy代理实例去完成真正的远程访问,并返回结果。
第四步:在Controller引入GitHubFeign服务,完成远程服务调用:
@RestController @RequestMapping(value = "/github", produces = MediaType.APPLICATION_JSON_VALUE) public class GithubController { @Resource private GitHubFeign gitHubFeign; /** * 查找github标准库信息 * * @author: jacklin * @since 2022/6/30 21:36 **/ @GetMapping(value = "/searchRepositories") String searchRepositories(@RequestParam(value = "q") String q) { return gitHubFeign.searchRepositories(q); } }
返回结果数据如下:
可以看出,feign使得远程调用跟本地方法是一样,极大的简化了rpc远程调用的方式。
三、OpenFeign和Feign的区别
可以认为OpenFeign是Feign的增强版,不同的是OpenFeign支持Spring MVC注解
Feign | OpenFeign |
---|---|
Feign 是Netflix公司写的,是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端,是SpringCloud中的第一代负载均衡客户端。 |
OpenFeign 的前身是Neflix Feign,Spring Cloud在Feign的基础上扩展支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient 可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。 |
OpenFeign
和Feign
底层都内置了Ribbon负载均衡组件,在导入OpenFeign
依赖后无需专门导入Ribbon
依赖,用做客户端负载均衡,去调用注册中心服务。
四、OpenFeign核心工作原理
- 通过 @EnableFeignCleints触发Spring应用程序对classpath中
@FeignClient
修饰类的扫描。 - 解析到 @FeignClient 修饰类后,
Feign
框架通过扩展Spring Bean Deifinition
的注册逻辑,最终注册一个FeignClientFacotoryBean
进入Spring
容器 - Spring容器在初始化其他用到 @FeignClient 接口的类时, 获得的是
FeignClientFacotryBean
产生的一个代理对象Proxy
。 - 基于Java原生的动态代理机制,针对
Proxy
的调用,都会被统一转发给Feign
框架所定义的一个 InvocationHandler,由该Handler
完成后续的HTTP转换,发送、接收以及HTTP响应的工作。
五、OpenFeign包扫描原理
要想通过OpenFeign实现远程调用,就涉及到一个OpenFeign的核心注解:@EnableFeignClient
,根据字面意思可以知道,该注解是开启OpenFeign的功能,一般都会被添加到我们的启动类上。
Spring包扫描的大体流程:
- 开启OpenFeign功能@EnableFeignClients,接着通过@Import(FeignClientsRegistrar.class) 这个import的方式导入FeignClientsRegistrar类,开启OpenFeign组件的加载。
@SpringBootApplication @EnableFeignClients public class MambaBlockDemoApplication { public static void main(String[] args) { SpringApplication.run(MambaBlockDemoApplication.class, args); } }
FeignClientsRegistrar负责Feign接口的加载,源码如下:
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //注册配置 registerDefaultConfiguration(metadata, registry); //注册FeignClient registerFeignClients(metadata, registry); }
- registerFeignClients()方法会调用findCandidateComponents()方法来查找指定路径的basePackages下所有被@FeignClients注解修饰的类、接口。
LinkedHashSetcandidateComponents = Set findCandidateComponents(String basePackage)
- 只保留被@FeignClient的修饰的接口。
for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); Mapattributes = annotationMetadata .getAnnotationAttributes(FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); //注入到Spring容器中 registerFeignClient(registry, annotationMetadata, attributes); } }
六、总结
本文通过远程调用的GitHub开放API用到的OpenFeign作为示例代码作为入口进行讲解。然后以图解+解读源码的方式深入剖析了OpenFeign的运行机制和架构设计
到此这篇关于Spring Cloud OpenFeign 远程调用的文章就介绍到这了,更多相关Spring Cloud OpenFeign 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!