SpringCloud原理-OpenFeign篇(四、请求原理)
文章目录
- 前言
- 正文
- 一、书接上回,从代理对象入手
- 二、ReflectiveFeign.FeignInvocationHandler#invoke()
- 三、SynchronousMethodHandler#invoke(...) 的实现原理
- 3.1 invoke(...)源码
- 3.2 executeAndDecode(...) 执行请求并解码
- 四、如何更换client 的实现
- 附录
- 附1:本系列文章链接
- 附2:比较HttpURLConnection、Apache HttpClient、OkHttp
前言
本篇是SpringCloud原理系列的 OpenFeign 模块的第四篇。
在我们启动完应用后,Spring容器也初始化好了很多我们用到的类。(什么,你不知道,烦请先看看第三篇)
那么我们下一步要做的就是,发出rest请求,然后调用FeignClient标注的接口方法。这篇文章,我们就来看看它的原理。
本文关键词:RequestTemplate、SynchronousMethodHandler
使用java 17,spring cloud 4.0.4,springboot 3.1.4
使用项目是本系列第一篇中的项目
正文
一、书接上回,从代理对象入手
第三篇文章时,我们看到了SpringCloud将 OpenFeign的接口,映射为一个代理对象。
打个比方,使用如下接口:
@FeignClient(name = "helloFeignClient", url = "http://localhost:10080")
public interface HelloFeignClient {@PostMapping("/hello/post")HelloResponse postHello(@RequestBody HelloRequest helloRequest);
}
最终生成的代理对象是对 HelloFeignClient 接口的代理,并且绑定了handler。handler的类型是ReflectiveFeign.FeignInvocationHandler

换句话说,就是当我们调用接口HelloFeignClient 中的方法时,会触发调用ReflectiveFeign.FeignInvocationHandler 的invoke(...)方法。
二、ReflectiveFeign.FeignInvocationHandler#invoke()

查看源码可以知道,这里invoke方法,实际是先从 dispatch中找到对应方法的真正的处理器,然后进行调用。
从第三篇文章,我们能知道 dispatch 是对 method 的映射。
比如接口HelloFeignClient 会被映射为dispatch,一个方法对应为一对key、value值。dispatch的类型是:
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
也就是说Method 只是作为一个桥梁,连接起了HelloFeignClient 内的方法和真正执行的handler实例。这里的实例真正的实现是SynchronousMethodHandler。也就是说,当我们调用接口方法时,会执行SynchronousMethodHandler#invoke(...)。
三、SynchronousMethodHandler#invoke(…) 的实现原理
3.1 invoke(…)源码
public Object invoke(Object[] argv) throws Throwable {// 创建请求模板,包装请求头、请求体,url等字段参数RequestTemplate template = this.buildTemplateFromArgs.create(argv);// 获取连接超时等参数Request.Options options = this.findOptions(argv);// 重试Retryer retryer = this.retryer.clone();while(true) {try {// 执行请求并解码return this.executeAndDecode(template, options);} catch (RetryableException var9) {RetryableException e = var9;try {retryer.continueOrPropagate(e);} catch (RetryableException var8) {Throwable cause = var8.getCause();if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {throw cause;}throw var8;}if (this.logLevel != Level.NONE) {this.logger.logRetry(this.metadata.configKey(), this.logLevel);}}}}
3.2 executeAndDecode(…) 执行请求并解码
Object executeAndDecode(RequestTemplate template, Request.Options options) throws Throwable {// 通过模版获取请求体,执行所有请求拦截器Request request = this.targetRequest(template);if (this.logLevel != Level.NONE) {this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);}long start = System.nanoTime();Response response;try {// 使用客户端执行请求response = this.client.execute(request, options);// 使用响应建造器构造一个响应体,包含请求和请求模板response = response.toBuilder().request(request).requestTemplate(template).build();} catch (IOException var9) {if (this.logLevel != Level.NONE) {this.logger.logIOException(this.metadata.configKey(), this.logLevel, var9, this.elapsedTime(start));}throw FeignException.errorExecuting(request, var9);}long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);// 处理响应结果&记录日志&响应解码return this.responseHandler.handleResponse(this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);}
通过分析,发现是先创建了RequestTemplate 实例,然后调用了client实例进行远程调用。而client的实现有多个,我这边看到内部实现了一个默认的:
public static class Default implements Client {public Response execute(Request request, Request.Options options) throws IOException {HttpURLConnection connection = this.convertAndSend(request, options);return this.convertResponse(connection, request);}
}
也就是说,到了这一步,就涉及到远程连接了。
这里用的是比较原始的HttpURLConnection。每次都创建新的连接,去请求,然后断开连接。这样很多时间也就浪费在建立连接等操作上了。而且调用量一旦变大,很容易出错。
问题来了,有没有什么办法能优化下呢?
四、如何更换client 的实现
上文提到 HttpURLConnection 是默认的连接方式。那麽我们有什么优化方案吗?
可替代方案一般有两种,一种是带有连接池的Apache HttpClient ,另一种是协议上占有优势的 OkHttp。
至于它们的更详细的优缺点,以及不同之处,请查看本文的附2。
另外,我的下一篇文打算单独将这块写一下 ===> SpringCloud实用-OpenFeign整合okHttp
戳附录中的【本系列文章链接】查看文章。
附录
附1:本系列文章链接
SpringCloud系列文章目录(总纲篇)
附2:比较HttpURLConnection、Apache HttpClient、OkHttp
参考:七大主流的HttpClient程序比较
| Client | 优点 | 缺点 |
|---|---|---|
| HttpURLConnection | jdk自带、原始、简单 | 缺乏连接池管理、域名机械控制等特性支持,性能&效率较低,一般不建议使用 |
| Apache HttpClient (已经停止开发) Apache HttpComponents HttpClient | 1. 支持连接池、多线程 2. 易用,灵活 | 安卓社区不再使用它,替换为了okHttp 需要自己做一层封装 |
| java.net.http.HttpClient | java11正式启用,替代原先的HttpURLConnection | 如果使用的版本是java11以下的,用不了它 |
| okHttp | 性能方面与HttpClient基本一样 链接复用 Response 缓存和 Cookie 默认 GZIP 请求失败自动重连 DNS 扩展 Http2/SPDY/WebSocket 协议支持 默认情况下,OKHttp会自动处理常见的网络问题:像二次连接、SSL的握手问题。 从Android4.4开始HttpURLConnection的底层实现采用的是okHttp. | |
一般情况下,如果使用了SpringCloud,基本都会选择 OpenFeign+okHttp的组合。
相关文章:
SpringCloud原理-OpenFeign篇(四、请求原理)
文章目录 前言正文一、书接上回,从代理对象入手二、ReflectiveFeign.FeignInvocationHandler#invoke()三、SynchronousMethodHandler#invoke(...) 的实现原理3.1 invoke(...)源码3.2 executeAndDecode(...) 执行请求并解码 四、如何更换client 的实现 附录附1&#…...
什么是工业物联网(IOT)?这样的IOT平台你需要吗?——青创智通
物联网(IOT)是指在互联网上为传输和共享数据而嵌入传感器和软件的互联设备的广泛性网络。这允许将从物理对象收集的信息(数据)存储在专用服务器或云中。通过分析这些积累的信息,通过提供最优的设备控制和方法,可以实现一个更安全、更方便的社会。在智能家…...
MTK Pump Express 快速充电原理分析
1 MTK PE 1.1 原理 在讲正文之前,我们先看一个例子。 对于一块电池,我们假设它的容量是6000mAh,并且标称电压是3.7V,换算成Wh(瓦时)为单位的值是22.3Wh(6000mAh*3.7V);普通的充电器输出电压电流是5V2A(10W),…...
leetcode刷题记录——1991. 找到数组的中间位置
找到数组的中间位置 给你一个下标从 0 开始的整数数组 nums ,请你找到 最左边 的中间位置 middleIndex (也就是所有可能中间位置下标最小的一个)。 中间位置 middleIndex 是满足 nums[0] nums[1] … nums[middleIndex-1] nums[middleInd…...
跨域攻击分析和防御(上)
点击星标,即时接收最新推文 跨域攻击 在大型公司或大型跨国企业中都拥有自己的内网,跨国公司都有各个子公司一般以建立域林进行共享资源。根据不同职能区分的部门,从逻辑上以主域和子域进行划分以方便统一管理。在物理层使用防火墙将各个子公…...
GEE:梯度提升树(Gradient Boosting Tree)分类教程(样本制作、特征添加、训练、精度、参数优化、贡献度、统计面积)
作者:CSDN @ _养乐多_ 本文将介绍在Google Earth Engine (GEE)平台上进行梯度提升树(Gradient Boosting Tree)分类的方法和代码,其中包括制作样本点教程(本地、在线和本地在线混合制作样本点,合并样本点等),加入特征变量(各种指数、纹理特征、时间序列特征、物候特征…...
ubuntu22.04 arrch64版在线安装redis
脚本 apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 echo "deb http://archive.ubuntu.com/ubuntu/ trusty main universe restricted multiverse" >…...
篮桥云课-摆玩具
思维好题 一开始掉进了二分的陷阱,发现看看逐个位置的差,我们要分成k段就是要取消k-1个最大的逐差 然后将剩余的加起来就可以了 因为本体保证是从小到大给出的 这一点保证了答案的正确性,自己没想出来 还是太菜了 #include<bits/stdc.h&…...
【python】python进阶知识点
eval函数用法 函数参数:字符串 # 计算表达式 print(eval("12*3")) # 把字符串当成python程序运行 print(eval("random.random()")) # 字符串转成列表 print(type(eval("[1,2,3,4]"))) # 字符串转成字典 print(type(eval("{name…...
LeetCode算法题解(动态规划)|LeetCode322. 零钱兑换、LeetCode279. 完全平方数
一、LeetCode322. 零钱兑换 题目链接:322. 零钱兑换 题目描述: 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一…...
Python Web开发基础知识篇
一,基础知识篇 本片文章会简单地说一些python开发web中所必须的一些基础知识。主要包括HTML和css基础、JavaScript基础、网络编程基础、MySQL数据库基础、Web框架基础等知识。 1,Web简介 Web,全称为World Wide Web,也就是WWW,万…...
企业计算机服务器中了360勒索病毒怎么办,360勒索病毒解密文件恢复
计算机技术的不断发展,为企业的生产运营提供了极大便利,不仅提升了办公效率,还促进了企业的发展。企业计算机在日常工作中一定加以防护,减少网络威胁事件的产生,确保企业的生产生产运营。最近,网络上的360后…...
LeetCode无重复字符的最长字符串的Java实现
题目 给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子字符串是 "abc",所以其长度为 3。示例 2: 输入: s "bbbbb" 输…...
opencv-图像平滑
高斯平滑 高斯平滑即采用高斯卷积核对图像矩阵进行卷积操作。高斯卷积核是一个近似服从高斯分布的矩阵,随着距离中心点的距离增加,其值变小。这样进行平滑处理时,图像矩阵中锚点处像素值权重大,边缘处像素值权重小。 import cv2 …...
【开源】基于Vue.js的天然气工程运维系统的设计和实现
项目编号: S 022 ,文末获取源码。 \color{red}{项目编号:S022,文末获取源码。} 项目编号:S022,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统角色分类2.2 核心功能2.2.1 流程…...
数据丢失抢救神器之TOP10 Android 数据恢复榜单
在快节奏的数字时代,我们的生活越来越与智能手机交织在一起,使它们成为重要数据和珍贵记忆的存储库。由于意外删除、软件故障或硬件故障而丢失数据可能是一种痛苦的经历。值得庆幸的是,技术领域提供了 Android 数据恢复软件形式的解决方案。这…...
梨花声音教育,动作电影中配音也能带来听见“冲击力”
在为动作电影提供配音服务时,配音员家需要通过声音的力量来增强画面上的动作张力、传递角色的活力,以及呈现出电影中的紧迫感。动作片充斥着快节奏的场景交替、紧张的格斗和惊险的逃逸等元素,配音需要精确、生动且充满动力。为动作电影配音应…...
Elaticsearch学习
Elaticsearch 索引 1、索引创建 PUT /index_v1 {"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings": {"properties": {"aaa": {"type": "keyword","store&qu…...
【腾讯云云上实验室】向量数据库+LangChain+LLM搭建智慧辅导系统实践
目录 一、搭建智慧辅导系统——向量数据库实践指南1.1、创建向量数据库并新建集合1.2、使用 TKE 快速部署 ChatGLM1.3、部署 LangChain PyPDFVectorDB等组件1.4、配置知识库语料1.5、基于 VectorDB LLM 的智能辅导助手 二、LLM时代的次世代引擎——向量数据库2.1、向量数据库L…...
从0开始学习JavaScript--深入了解JavaScript框架
JavaScript框架在现代Web开发中扮演着关键角色,为开发者提供了丰富的工具和抽象层,使得构建复杂的、高性能的Web应用变得更加容易。本文将深入探讨JavaScript框架的核心概念、常见框架的特点以及它们在实际应用中的使用。 JavaScript框架的作用 JavaSc…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
