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标注的接口方法。这篇文章,我们就来看看它的原理。

本文关键词:RequestTemplateSynchronousMethodHandler

使用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.FeignInvocationHandlerinvoke(...)方法。

二、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优点缺点
HttpURLConnectionjdk自带、原始、简单缺乏连接池管理、域名机械控制等特性支持,性能&效率较低,一般不建议使用
Apache HttpClient (已经停止开发)
Apache HttpComponents HttpClient
1. 支持连接池、多线程
2. 易用,灵活
安卓社区不再使用它,替换为了okHttp
需要自己做一层封装
java.net.http.HttpClientjava11正式启用,替代原先的HttpURLConnection如果使用的版本是java11以下的,用不了它
okHttp性能方面与HttpClient基本一样
链接复用
Response 缓存和 Cookie
默认 GZIP
请求失败自动重连
DNS 扩展
Http2/SPDY/WebSocket 协议支持
默认情况下,OKHttp会自动处理常见的网络问题:像二次连接、SSL的握手问题。
从Android4.4开始HttpURLConnection的底层实现采用的是okHttp.

一般情况下,如果使用了SpringCloud,基本都会选择 OpenFeign+okHttp的组合。

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

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

相关文章

【Linux】记录错误信息日志的实现

文章目录 前言一、 目录实现&#xff08;log.hpp&#xff09;二、目录的具体使用1.comm.hpp&#xff08;管道初始化&#xff09;2.sever.cpp&#xff08;为读端且令其创建命名管道&#xff09;3.client.cpp(为写端) 前言 我们这个设计的日志可以自定以输出的方向&#xff0c;可…

领域驱动设计总结——如何构造领域模型

领域驱动设计总结——如何构造领域模型 本文为领域驱动设计系列总结的第三篇&#xff0c;主要对领域驱动设计概念做个介绍&#xff0c;本系列领域驱动设计总结主要是在Eric Evans 所编写的《领域驱动设计》 一书的基础上进行归纳和总结。本文主要介绍在领域驱动设计中如何构造…

OpenWrt Lan口上网设置

LAN口上网设置 连接上openwrt&#xff0c;我用的 倍控N5105&#xff0c;eth0&#xff0c;看到Openwrt的IP是10.0.0.1 在 网络 -> 网口配置 -> 设置好 WAN 口和 LAN 口 初次使用经常重置 openwrt 所以我设置的是 静态IP模式 - 网络 -> 防火墙 -> 常规设置 ->…

【深度学习实验】图像处理(二):PIL 和 PyTorch(transforms)中的图像处理与随机图片增强

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入需要的工具包1. PIL图像处理a. 生成绿色和蓝色图像b. 缩放和合成图像c 在合成图像上添加文字d. 展示并保存图像 2. PIL随机图像增强a. 定义随机图像增强函数b. 实验结果展示 3. PyTorch&…

FloodFill

"绝境之中才窥见&#xff0c;Winner&#xff0c;Winner" FloodFill算法简介: floodfill又翻译成漫水填充。我们可以将下面的矩阵理解为一片具有一定高度的坡地&#xff0c;此时突发洪水&#xff0c;洪水会将高度<0的地方填满。 话句话来说&#xff0c;Fl…

uniapp+vue基于Android的校园二手跳蚤市场的设计与实现 微信小程序

实现功能&#xff1a; 用户管理&#xff1a;登陆、注册、注销、修改密码、上传头像、修改资料 发布与检索&#xff1a;发布商品、模糊搜索、人气排序、价格排序、时间排序、推送商品&#xff08;协同过滤算法实现个性化推荐&#xff09;&#xff0c;最新发布、分类检索 核心交易…

解密Kafka主题的分区策略:提升实时数据处理的关键

目录 一、Kafka主题的分区策略概述1.1 什么是Kafka主题的分区策略&#xff1f;1.2 为什么分区策略重要&#xff1f; 二、Kafka默认分区策略2.1 Round-Robin分区策略 三、自定义分区策略3.1 编写自定义分区器3.2 最佳实践&#xff1a;如何选择分区策略 四、分区策略的性能考量4.…

【数据中台】开源项目(2)-Dbus数据总线

1 背景 企业中大量业务数据保存在各个业务系统数据库中&#xff0c;过去通常的同步数据的方法有很多种&#xff0c;比如&#xff1a; 各个数据使用方在业务低峰期各种抽取所需数据&#xff08;缺点是存在重复抽取而且数据不一致&#xff09; 由统一的数仓平台通过sqoop到各个…

error LNK2038: 检测到“RuntimeLibrary”的不匹配项 解决方法

问题&#xff1a; 我们在使用Visual Studio编程的时候偶尔会遇到以下三种报错&#xff1a; error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MDd_DynamicDebug” &#xff08;引用的是release模式&#xff0c;但设置成debug模式了…

【研究中2】sql server权限用户设置

--更新时间2023.11.26 21&#xff1a;30 负责人&#xff1a;jerrysuse DBAliCMSIF EXISTS (select * from sysobjects where namehkcms_admin)--判断是否存在此表DROP TABLE hkcms_adminCREATE TABLE hkcms_admin (id int identity(1, 1),--id int primary key identity…

本地运行“李开复”的零一万物 34B 大模型

这篇文章&#xff0c;我们来聊聊如何本地运行最近争议颇多的&#xff0c;李开复带队的国产大模型&#xff1a;零一万物 34B。 写在前面 零一万物的模型争议有很多&#xff0c;不论是在海外的社交媒体平台&#xff0c;还是在国内的知乎和一种科技媒体上&#xff0c;不论是针对…

【Spring】Spring事务失效问题

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

058-第三代软件开发-文件Model

第三代软件开发-文件Model 文章目录 第三代软件开发-文件Model项目介绍文件Model 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;…

智能头盔天眼摄像头、单兵执法记录仪等配合MESH自组网在应急指挥调度中的应用

智能头盔、天眼摄像头、头盔记录仪、头盔摄像头、单兵执法记录仪等配合MESH自组网在应急指挥调度中的应用。 20人背负单兵自组网&#xff08;带手咪&#xff09;到训练场&#xff0c;戴头盔&#xff0c;头盔上放头盔式摄像头&#xff0c;大功率自组网设置在制高点&#xff0c;…

【办公软件】电脑开机密码忘记了如何重置?

这个案例是家人的电脑&#xff0c;已经使用多年&#xff0c;又是有小孩操作过的&#xff0c;所以电脑密码根本不记得是什么了&#xff1f;那难道这台电脑就废了吗&#xff1f;需要重新装机吗&#xff1f;那里面的资料不是没有了&#xff1f; 为了解决以上问题&#xff0c;一般…

数据结构——哈夫曼树结构总结

一直在找工作&#xff0c;没时间写博客&#xff0c;现在找到工作了&#xff0c;博客回归~ 哈夫曼树定义及构建教程

C#,《小白学程序》第三课:类class,类的数组及类数组的排序

类class把数值与功能巧妙的进行了结合&#xff0c;是编程技术的主要进步。 下面的程序你可以确立 分数 与 姓名 之间关系&#xff0c;并排序。 1 文本格式 /// <summary> /// 同学信息类 /// </summary> public class Classmate { /// <summary> /…

IDM(Internet Download Manager)PC版提升下载速度与效率的利器

你是否曾经因为下载速度慢而感到烦恼&#xff1f;或者在下载大型文件时&#xff0c;经历了长时间的等待&#xff1f;如果你有这样的困扰&#xff0c;那么IDM&#xff08;Internet Download Manager&#xff09;就是你的救星&#xff01; IDM是一款高效、实用的下载管理器&…

Day42力扣打卡

打卡记录 统计子串中的唯一字符&#xff08;找规律&#xff09; 链接 大佬的题解 class Solution:def uniqueLetterString(self, s: str) -> int:ans total 0last0, last1 {}, {}for i, c in enumerate(s):total i - 2 * last0.get(c, -1) last1.get(c, -1)ans tot…

如何深刻理解从二项式分布到泊松分布

泊松镇贴 二项分布和泊松分布的表达式 二项分布&#xff1a; P ( x k ) C n k p k ( 1 − p ) n − k P(xk) C_n^kp^k(1-p)^{n-k} P(xk)Cnk​pk(1−p)n−k 泊松分布&#xff1a; P ( x k ) λ k k ! e − λ P(xk) \frac{\lambda^k}{k!}e^{-\lambda} P(xk)k!λk​e−…