写点什么

Spring Cloud OpenFeign - 远程调用

作者:java易二三
  • 2023-08-17
    湖南
  • 本文字数:3020 字

    阅读完需:约 10 分钟

一、什么是 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 标准库信息

  • <p>

  • 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 {


@Resourceprivate 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 核心工作原理 1️⃣ 通过 @EnableFeignCleints 触发 Spring 应用程序对 classpath 中 @FeignClient 修饰类的扫描。2️⃣ 解析到 @FeignClient 修饰类后,Feign 框架通过扩展 Spring Bean Deifinition 的注册逻辑,最终注册一个 FeignClientFacotoryBean 进入 Spring 容器 3️⃣ Spring 容器在初始化其他用到 @FeignClient 接口的类时, 获得的是 FeignClientFacotryBean 产生的一个代理对象 Proxy。4️⃣ 基于 Java 原生的动态代理机制,针对 Proxy 的调用,都会被统一转发给 Feign 框架所定义的一个 InvocationHandler,由该 Handler 完成后续的 HTTP 转换,发送、接收以及 HTTP 响应的工作。


五、OpenFeign 包扫描原理要想通过 OpenFeign 实现远程调用,就涉及到一个 OpenFeign 的核心注解:@EnableFeignClient,根据字面意思可以知道,该注解是开启 OpenFeign 的功能,一般都会被添加到我们的启动类上。


Spring 包扫描的大体流程:


开启 OpenFeign 功能 @EnableFeignClients,接着通过 @Import(FeignClientsRegistrar.class) 这个 import 的方式导入 FeignClientsRegistrar 类,开启 OpenFeign 组件的加载。@SpringBootApplication@EnableFeignClientspublic class MambaBlockDemoApplication {


public static void main(String[] args) {    SpringApplication.run(MambaBlockDemoApplication.class, args);}
复制代码


}FeignClientsRegistrar 负责 Feign 接口的加载,源码如下:@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//注册配置 registerDefaultConfiguration(metadata, registry);//注册 FeignClientregisterFeignClients(metadata, registry);}registerFeignClients()方法会调用 findCandidateComponents()方法来查找指定路径的 basePackages 下所有被 @FeignClients 注解修饰的类、接口。LinkedHashSet<BeanDefinition> candidateComponents = Set<BeanDefinition> findCandidateComponents(String basePackage)只保留被 @FeignClient 的修饰的接口。for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");


  Map<String, Object> attributes = annotationMetadata        .getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); //注入到Spring容器中 registerFeignClient(registry, annotationMetadata, attributes);
复制代码


}}六、总结本文通过远程调用的 GitHub 开放 API 用到的 OpenFeign 作为示例代码作为入口进行讲解。然后以图解+解读源码的方式深入剖析了 OpenFeign 的运行机制和架构设计。

需要资料!点击这里!

用户头像

java易二三

关注

还未添加个人签名 2021-11-23 加入

还未添加个人简介

评论

发布
暂无评论
Spring Cloud OpenFeign - 远程调用_Java_java易二三_InfoQ写作社区