【Spring Cloud 进阶】OpenFeign 底层原理解析

参考文章

  • 万字+33张图探秘OpenFeign核心架构原理 | 三友
  • SpringCloud OpenFeign源码详细解析
  • Java 代理机制

OpenFeign 是一个精彩的使用动态代理技术的典型案例,通过分析其底层实现原理,我们可以对动态代理技术有进一步的理解。

目录

    • 1. Feign 与 OpenFeign
      • 1.1 OpenFeign 的使用
      • 1.2 Feign 的原生使用
    • 2. Feign 生成代理实例的原理
      • 2.1 Feign 大致原理
      • 2.2 Feign 的核心组件一览
      • 2.3 动态代理生成原理
        • 2.3.1 Feign 的 Builder
        • 2.3.2 Feign Builder 的 target 函数
        • 2.3.3 build 出来的 Feign 实例是什么
        • 2.3.4 ReflectiveFeign 是如何创建出代理类的
        • 2.3.5 InvocationHandler 的 invoke 方法
        • 2.3.6 总结动态代理的生成逻辑 ⭐⭐⭐⭐⭐
    • 3. Feign 的一次 HTTP 调用执行的过程
    • 4. 总结

1. Feign 与 OpenFeign

Feign 由 Netflix 开源,Spring Cloud 对 Feign 进行了封装整合从而形成了 OpenFeign 项目。所以这篇文章之后的内容不再可以区分 Feign 和 OpenFeign。

在 Spring Cloud 项目中,以下代码可以引入 OpenFeign 依赖:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

1.1 OpenFeign 的使用

在 Spring Cloud 中使用 OpenFeign 还是很简单的,可以参考 Feign 远程调用

1.2 Feign 的原生使用

OpenFeign 是 Spring Cloud 官方对 Feign 封装整合之后的用法,原生 Feign 的用法有些区别。

加入我们想通过 Feign 来调用 https://tenapi.cn/v2/yiyan 这个公开接口,我们可以这样做:

  • 首先声明一个 TenAPIClient 的 interface:
public interface TenAPIClient {

    @RequestLine("GET /v2/yiyan")
    String yiYan();
}
  • 使用:
public class OrderFeignDemo {

    public static void main(String[] args) {
        TenAPIClient tenAPIClient = Feign.builder()
                .target(TenAPIClient.class, "https://tenapi.cn");
        String s = tenAPIClient.yiYan();
        System.out.println(s);
    }
}

在上面例子中,我们可以看到,我们只需要声明一个 TenAPIClient 的 interface,以及 API 的调用函数声明,Feign 就可以通过动态代理技术生成一个实现了能够发起远程调用这个 API 的 client 实例。这就是 Feign 对动态代理技术的妙用。

2. Feign 生成代理实例的原理

这里的源码基于 Spring Boot 3.2.3,Spring Cloud 2023.0.0

Feign 根据我们编写的 interface 动态生成一个实现了这个 interface 的代理实例,我们的代码就是通过这个代理实例实现了真正的远程调用 API 的逻辑。这一节就看看 Feign 是如何生成这个代理实例的。

2.1 Feign 大致原理

下图展示了 Feign 框架的原理,它根据 interface 使用动态代理生成代理类,我们使用代理类发起了 HTTP 请求并得到响应结果。
Feign 大致原理

2.2 Feign 的核心组件一览

在我们的 main 代码中的 Feign.builder() .target(TenAPIClient.class, "https://tenapi.cn"); 用来生成代理类,这里的 builder 中的成员就是 Feign 的各个核心组件了,我们来看一下:

Builder
在这里插入图片描述
简要介绍一下各个组件的作用:

  • Client:一个 HTTP Client 的接口,具体实现可以是 JDK 内置的 HttpURLConnection,也可以是 Apache HttpClient 或 OkHttp,Feign 使用这个 Client 来发送 HTTP 请求。
  • Contract:解析 interface 中方法的注解和参数,用于得知各个 API 的相关信息。这个组件在代码中通过解析各个方法的注解和参数(比如 @RequestLine("GET /v2/yiyan")),获得 List<MethodMetadata>,即各个 method 的 meta data,比如 URL、body、header 等等信息。
  • Retryer:用于实现重试的逻辑
  • RequestInterceptorResponseInterceptor:是一个拦截接口,通过这个接口,可以实现在 Http 请求发送之前或接收到响应之后对内容进行修改。
  • EncoderDecoder:用于序列化请求和反序列化响应的接口
  • InvocationHandlerFactory:用于创建 InvocationHandler,我们都知道,JDK 动态代理就是通过 InvocationHandler 来生成代理实例的,所以 InvocationHandler 的 invoke() 方法的逻辑就是动态代理走的核心逻辑。

2.3 动态代理生成原理

关于如何通过动态代理来获得 TenAPIClient 的实现类,其实就是分析 Feign.builder().target(TenAPIClient.class, "https://tenapi.cn"); 这一行代码在内部做了什么工作。

Feign 这个类是什么呢?在源码中有这样一行注释:

In implementation, Feign is a factory for generating targeted http apis.

所以,Feign 就是一个用来创建 TenAPIClient 实例的 factory。

2.3.1 Feign 的 Builder

Feign.builder() 会创建一个 Builder 类,用来创建 Feign,这个 Builder 类描述了 Feign 的各个关键组件,即 2.2 节介绍的那些,比如 Contract、Client、Retryer 等等,当我们需要 Feign 满足一些我们特殊需求时,我们可以通过 Builder 来替换个别关键组件,比如替换 HTTP Client 的实现、替换 Retryer 来更换重试策略等。在 Spring Cloud 整合 Feign 时,就通过这里来替换了一些关键组件,比如替换了重试策略等。

下面看一下 Feign.builder().target(TenAPIClient.class, "https://tenapi.cn"); 中 target 所做的事情:

target 实现
我们调用的是第一个 target 函数,传入了 TenAPIClient.class 和 baseURL,这样他就生成了一个 TenAPIClient 的代理实现类。

2.3.2 Feign Builder 的 target 函数

在第一个 target 函数中,他使用 apiType 和 url 初始化了一个 HardCodedTarget 类实例,HardCodedTarget 类实现了 Target 接口,这个接口是用来将一个 RequestTemplate 实例转换为一个真正的 Request 实例的。

第一个 target 函数调用了第二个 target 函数,第二个 target 函数就复杂了,它先 build() 创建了一个 Feign 实例,我们知道 Feign 就是一个 factory,这个 factory 创建出代理类,就如图中 target 函数的实现一样,build 出一个 Feign 实例后,就通过 newInstance() 创建出一个代理类。

所以现在来看看 build() 创建出的 Feign 实例是什么,以及 Feign 实例如何创建出代理类的。

2.3.3 build 出来的 Feign 实例是什么

build() 函数最终通过如下函数来构建出 Feign 实例:

build函数

这里有两个需要注意的点:

  • 我们生成的 Feign 实例是 ReflectiveFeign 的类实例(因为 Feign 只是一个抽象类)
  • 我们有了一个 SynchronousMethodHandler 的 factory,它用来生成 MethodHandler。MethodHandler 是用来实现代理类中各个方法调用的逻辑的,比如我们在 TenAPIClient 中声明的 yiYan() 方法的具体逻辑就是由一个 MethodHandler 来实现的。

既然我们知道了 Feign 的实现类就是 ReflectiveFeign,那我们就仔细看一下 ReflectiveFeign 的代码。

2.3.4 ReflectiveFeign 是如何创建出代理类的

这个问题的代码都在 ReflectiveFeign 的 newInstance() 方法中,我们来看一下:

newInstance

上图中被红色方框圈中的代码是核心代码,我们重点看这一部分。

其中,methodToHandler 是一个从 Method 到 MethodHandler 的 map,Method 也就是我们在 TenAPIClient 接口中定义的方法,MethodHandler 我们之前提到了,就是用来处理这个方法的具体逻辑,即包含真正执行远程调用的逻辑。targetToHandlersByName.apply(target, requestContext); 创建出这个 map 的原理大概就是,通过 Contract 解析出各个方法的 MethodMetaData,然后根据 Target、MethodMetaData 创建出实现了这个远程调用逻辑的 MethodHandler,从而构建出这个 map,这部分代码如下:

在这里插入图片描述
然后,我们接着看前面 newInstance 的核心代码,在得到 Method -> MethodHandler 的 map 后,接着创建出 InvocationHandler,学过 JDK 动态代理的都知道,InvocationHandler 是用来创建出最终的代理类的东西,这部分忘了的可以看一下 Java 代理机制 的 JDK 动态代理部分。

这里的 Proxy、InvocationHandler 都是 JDK 定义的,也是 JDK 实现的动态代理技术。所以 Feign 只需要按照 JDK 的要求去实现出自己的 InvocationHandler 实现,就能创建出一个实现了指定接口的动态代理类实例。

InvocationHandler 实现了代理的逻辑,当我们在 main 中写出 tenAPIClient.yiYan(); 时,其实就是调用了 InvocationHandler 实例的 invoke() 方法,这里的 invoke 方法就实现了向 API 发送 HTTP 请求并接收响应的逻辑。

在得到 InvocationHandler 的实例后,就通过 Proxy.newProxyInstance() 并传入 classLoader、所需要代理的类(在我们例子中就是 TenAPIClient)和 InvocationHandler,然后 JDK 动态代理就生成了一个代理类,也就是我们 main demo 中的 tenAPIClient 变量实例。

当我们调用 tenAPIClient 的 yiYan() 方法时,它其实就是调用了 InvocationHandler 的 invoke 方法,所以我们需要看一下这里 invoke 方法的实现是什么,它是如何实现了向 API 发送 HTTP 请求的。

2.3.5 InvocationHandler 的 invoke 方法

这里所说的 InvocationHandler 的实现类是 FeignInvocationHandler

FeignInvocationHandler

可以看到 invoke 的实现很简单,关键在于 return 后面这一行,就是根据你调用的函数是什么,找到相应的 MethodHandler,然后调用 MethodHandler 的 invoke 方法,执行 MethodHandler 中存放的远程调用的代码逻辑。比如 tenAPIClient.yiYan(); 这个调用就是调用的 FeignInvocationHandler 的 invoke 方法,invoke 方法知道你调用的方法名是 yiYan,然后找到这个 method handler,然后 method handler 执行其远程调用的逻辑,实现最终的远程调用。

2.3.6 总结动态代理的生成逻辑 ⭐⭐⭐⭐⭐

在这里插入图片描述
总结如下:我们编写出 TenAPIClient 的接口和接口方法,Feign 通过 Contract 解析这个接口和其中的方法,得到各方法的 MethodMetaData,然后由此创建出各方法的 MethodHandler,再利用这些 method handlers 实现出 InvocationHandler,使用这个 InvocationHandler 利用 JDK 动态代理技术创建出动态代理对象。

3. Feign 的一次 HTTP 调用执行的过程

前面介绍原理时,也介绍的差不多了。当我们调用接口的方法时,动态代理技术会交由 InvocationHandler 的 invoke 方法来处理,invoke 方法根据你调用的方法名找到相应的 method handler 并执行相应的远程调用逻辑。用图示来解释:

在这里插入图片描述
以上就是 Feign 一次 HTTP 调用的执行过程。

4. 总结

以上介绍了 Netflix 开源的 Feign 的实现原理,它精彩地运用了动态代理技术,实现了只需要我们写出接口方法,Feign 就生成了具体的远程调用逻辑,而我们只需要关注远程调用的 API 相关参数即可。

本篇文章未涉及 Spring Cloud 如何整合 Feign,关于整合 Feign 的部分在之后再深入研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/417920.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VUE3:统计分析页面布局+自适应页面参考

一、布局 <template><div class"container1"><div class"form white"><el-form :inline"true" :rules"rules" :model"queryParams" label-width"80px" ref"querParmRef"><e…

力扣递归:路径总和

思路&#xff1a;此题思路为递归实现&#xff0c;递归思路为&#xff1a;在每层递归的过程中将各个节点的数据记录下来&#xff0c;不断将减少目标数据的值准备进行判断&#xff0c;当进行到叶子节点时要进行判断 /*** Definition for a binary tree node.* struct TreeNode {…

OJ_二叉树最短路径长度

题干 C实现 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vector> using namespace std;struct TreeNode {int num;TreeNode* left;TreeNode* right;TreeNode* parent; };void createTree(vector<TreeNode*>& nodeArr, int n) {for (i…

2000-2022年上市公司绿色专利申请占比/数据

2000-2022年上市公司绿色专利申请占比数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;国家知识产权局、WIPO绿色专利清单 3、指标&#xff1a;年份、股票代码、股票简称、行业代码、省份、城市、区县、行政区划代码、城市代码、区县代码、首次上市年份、上市状态、…

又降价啦!2024年阿里云核心产品价格全线下调,最高幅度达55%

2024年3月1日开始&#xff0c;阿里云将开启新一轮的降价政策&#xff0c;核心产品价格全线下调&#xff0c;平均降幅20%&#xff0c;最高幅度达55%&#xff0c;阿里云希望通过此次大规模降价&#xff0c;让更多企业和开发者用上先进的公共云服务&#xff0c;加速云计算在中国各…

深度学习 精选笔记(8)梯度消失和梯度爆炸

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

备战蓝桥杯Day19 - 堆排序基础知识

一、每日一题 - 填充 详细题解 s input() # 输入字符串 n len(s) # 定义字符的长度 judge ["00", "11", "0?", "1?", "?0", "?1", "??"] # 把所有的情况一一列举出来 count 0 # 设置计数…

【Python】PyGameUI控件

哈里前段时间写了一个windows平板上自娱自乐&#xff08;春节和家人一起玩&#xff09;基于pygame的大富翁游戏。 pygame没有按钮之类的UI控件&#xff0c;写起来不怎么顺手。就自己写一个简单的框架。 仓库地址 哈里PygameUi: pygame ui封装自用 (gitee.com) 使用示例 示…

Tomcat 软件和配置文件 基本介绍

一 &#xff0c;web知识 简介 &#xff08;一&#xff09;web技术 1&#xff0c;http协议和 B/S &#xff08;Browser/Server&#xff09;结构 最早出现了CGl (Common Gateway Interface)通用网关接口&#xff0c;通过浏览器中输入URL直接映射到一个服务器端的脚本程序执行&…

从单体服务到微服务:多模式 Web 应用开发记录<三>预初始化属性

相关文章&#xff1a; 多模式 Web 应用开发记录<一>背景&全局变量优化多模式 Web 应用开发记录<二>自己动手写一个 Struts 开头先看一个简单的例子&#xff0c;这是 ftl 文件的一个表单&#xff1a; <form id"validateForm" action"#&quo…

【程序员是如何看待“祖传代码”的?】《代码的遗产:探索程序员眼中的“祖传代码”》

程序员是如何看待“祖传代码”的&#xff1f; 在程序员的世界里&#xff0c;代码不仅仅是构建软件的基石&#xff0c;它们也承载着历史、智慧和技术的演变。在我的编程生涯中&#xff0c;我遇到过许多神奇而独特的“祖传代码”&#xff0c;这些代码如同古老的魔法书&#xff0…

【C语言】三子棋

前言&#xff1a; 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏规则是双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利。但因棋盘太小&#xff0c;三子棋在很多时候会出现和…

C++面试常见八股分享

1.unordered_set和set&#xff0c;unordered_map和map的区别 set 和 map 是 C STL 中的两种关联容器&#xff0c;而 unordered_set 和 unordered_map 是 C11 新增的基于哈希表的关联容器。它们之间的主要区别在于底层的数据结构和操作复杂度。 set 和 unordered_set&#xff1…

黑马程序员——接口测试——day05——Request库、Cookie、Session、UnitTest框架

目录&#xff1a; Requests库 Requests库安装和简介设置http请求语法应用案例 案例1案例2案例3案例4Cookie Cookie简介CookieSession认证方式案例5-看演示&#xff0c;此代码不需实现Session Session简介Session自动管理Cookie案例6面试题Cookie和Session区别获取指定响应数据…

云原生架构技术揭秘:探索容器技术的奥秘

云原生的概念和演进都是围绕云计算的核心价值展开的&#xff0c;比如弹性、自动化、韧性&#xff0c;所以云原生所涵盖的技术领域非常丰富。 随着云计算技术的不断发展&#xff0c;云原生架构已经成为了新一代软件开发的重要趋势。本文将为您介绍云原生架构的相关技术&#xf…

单片机精进之路-9ds18b20温度传感器

ds18b20复位时序图&#xff0c;先将b20的数据引脚拉低至少480us&#xff0c;然后再将数据引脚拉高15-60us&#xff0c;再去将测传感器的数据引脚是不是变低电平并保持60-240us&#xff0c;如果是&#xff0c;则说明检测到温度传感器&#xff0c;并正常工作。需要在240us后才能检…

鸿蒙真有前景吗?是真是假?

直到“纯血鸿蒙”发布&#xff0c;才看清华为真正的布局&#xff0c;这一招实在是高明&#xff01; “纯血鸿蒙”发布之前&#xff0c;国内大批人唱衰华为&#xff0c;唱衰鸿蒙系统的生态&#xff0c;认为大概率会走诺基亚和微软的老路&#xff0c;没想到“纯血鸿蒙”一经推出…

高质 智能 绿色低碳棒线材轧制 智能测径仪等亦起关键作用

第十一届棒线材会议围绕推动轧钢装备数字化、智能化、绿色化转型升级&#xff0c;实现高质量发展&#xff0c;高质量、智能化、绿色低碳主题&#xff0c;将于4月22-24日在贵州省六盘水市召开。这也是轧钢生产近几年的发展趋势。 在线棒材生产中&#xff0c;蓝鹏测控可提供三种类…

每天十条linux知识点-24-0226(1)

文章目录 1.在哪下载linux内核源码&#xff1f;2.linux文件夹都有哪些文件&#xff1f;arch&#xff1a;包含和硬件体系结构相关的代码&#xff0c;每种平台占一个相应的目录&#xff0c;如i386、arm、arm64、powerpc、mips等。block&#xff1a;块设备驱动程序I/O调度。certs&…

又降价啦!2024年腾讯云服务器优惠价格表,不看后悔!

腾讯云服务器多少钱一年&#xff1f;62元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器218元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…