OpenFeign 的超时重试机制以及底层实现原理

目录

1. 什么是 OpenFeign?

2. OpenFeign 的功能升级

3. OpenFeign 内置的超时重试机制

3.1 配置超时重试

3.2 覆盖 Retryer 对象

4. 自定义超时重试机制

4.1 为什么需要自定义超时重试机制

4.2 如何自定义超时重试机制

5. OpenFeign 超时重试的底层原理

5.1 超时重试原理


1. 什么是 OpenFeign?

        OpenFeign 是一款基于 Feign 的声明式的 Web 服务客户端,它使得编写Web服务客户端变得更加容易。它可以帮助你轻松调用远程服务的工具。

【举个例子】

        想象一下你在使用手机的微信和朋友聊天,你只需要知道朋友的微信号就可以给他发消息,不需要知道他的手机号或者其他复杂的信息。OpenFeign 也是这样,它允许你在编写代码时,只需要知道你想调用的服务的名字和需要交互的部分(比如服务中的某个功能或接口),你不需要处理底层的网络连接或者复杂的HTTP请求过程,这些都由OpenFeign自动帮你完成。它提供了一种类似于调用本地方法的感觉来访问远程服务,从而让开发者可以专注于编写业务代码,而不是底层的网络通信细节。

2. OpenFeign 的功能升级

OpenFeign 在 Feign 的基础上还提供了增强、扩展功能:

  1. 更好的集成SpringCloud组件

    • OpenFeign与Spring Cloud的其他组件(如服务发现、负载均衡)紧密集成,它能够自动利用服务发现和负载均衡的功能,无需额外配置。
  2. 支持@FeignClient注解

    • OpenFeign引入了@FeignClient注解,使得声明式的客户端创建变得简单。你只需要在接口上使用@FeignClient指定服务名即可,而无需创建具体的RestTemplate或者使用URL硬编码远程服务调用。
  3. 错误处理改进

    • OpenFeign提供了更为人性化的错误处理方式。它允许你通过自定义错误解码器来对特定的错误响应进行处理。这样你可以捕捉并处理远程服务调用中的异常,使得异常管理更加灵活和精确。(报错信息也更加细化、明确了)
  4. 更丰富的配置项

    • OpenFeign提供了比原生Feign更为详尽的配置项,比如超时设置、重试策略。

3. OpenFeign 内置的超时重试机制

        在微服务架构中,服务之间是通过网络进行通信的,而网络又是非常复杂和不稳定的,所以在服务调用的过程中可能会失败或超时,那么在这种情况下,OpenFeign 就需要超时重试机制来解决了。

什么是超时重试 ?

        当你的服务请求因为网络问题或服务延迟等原因没能在预定时间内得到响应,超市重试机制会帮你自动重新发送请求。就像你用浏览器打开网页,如果一时加载不出来,你可能会点击刷新重试。OpenFeign 的超时重试就是自动帮你做这件事,尝试几次后如果还是不行,就会告诉你请求失败。这样可以提高服务的可靠性,防止因为网络不稳定、服务不可用、响应延迟等不确定性因素导致服务不可用。

       OpenFeign 默认情况下是不会自动开启超时重试机制的,所以想要使用超时重试功能,需要手动配置:

  1. 配置超时重试
  2. 覆盖 Retryer 对象

        后续的操作都是基于上篇博客中的案例进行演示,在 SpringBoot 整合 Nacos 的案例中,已经涵盖了 OpenFeign 的基础使用了,不会的可以先去看看:https://blog.csdn.net/xaiobit_hl/article/details/134142521

3.1 配置超时重试

spring:application:name: nacos-consumer-democloud:nacos:discovery:server-addr: localhost:8848username: nacospassword: nacosregister-enabled: false  #消费者(不需要将此服务注册到 nacos)openfeign:client:config:default: # 全局配置connect-timeout: 1000  # 连接超时时间(毫秒)read-timeout: 1000  # 读取的超时时间(毫秒)

3.2 覆盖 Retryer 对象

@Configuration  // 存储 Ioc
public class RetryerConfig {@Beanpublic Retryer retryer() {return new Retryer.Default(1000,  // 重试间隔时间1000,  // 最大重试间隔时间3  // 最大重试次数);}
}

PS:设置的最大重试次数为 3 次,最大重试间隔时间为 1s,重试间隔时间是 1s。

修改服务提供者代码 >>

        配置信息里边设置的读取超时时间为 1 秒,就是为了触发超时重试机制,按道理来说,这个时候我们的消费者就需要在接口里边 sleep 1秒,为了更好的看见问题,可以设置 1.5 秒:

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate ServletWebServerApplicationContext context;// 服务@RequestMapping("/getnamebyid")public String getNameById(Integer id) throws InterruptedException {System.out.println("------- do provider getNameById method" +LocalDateTime.now());Thread.sleep(1500); // 休眠 1.5 sreturn "provider-name-" + id +" | port:" + context.getWebServer().getPort();}
}

PS:① 启动一个临时服务实例,② 保护阈值设为 0 ,③ 启动消费者。

如果有多个实例,客户端默认的负载均衡是轮询,会导致现象并不明显; 如果有保护阈值,也会导致现象更复杂,所以搞一个服务实例,保护阈值不要去设置。

使用 http://localhost:8080/getnamebyid?id=2 获取服务:

此时服务已经获取不到了, 并且已经触发超时重试机制了,打开服务提供者的控制台:

总共打印了三次日志,因为我们设置的最大重试次数就是 3。

为什么不是  4 次呢?为什么日志的时间间隔是 2s 打印一次呢 ?

① 为什么不是打印 4 次 ?

因为 Retryer 的 Default 方法源码中重试次数变量 attempt 是从 1 开始的,然后核心方法 continueOrPropagate 中的 if 判断是当 attemp >= maxAttempts 时,才抛出异常。

下标从 1 开始 :

 if 判断:

② 为什么日志的时间间隔是 2s 打印一次呢 ? 

我们设置的连接超时时间是 1s,此处肯定是能连的上,所以跟 connect-timeout 没关系。

此处是因为我们设置的读取超时时间(read-timeout)是 1s,并且我们设置了一个间隔时间,也是 1s,所以每次打印日志的间隔时间就是 2s。

4. 自定义超时重试机制

4.1 为什么需要自定义超时重试机制

        因为 OPenFeign 内置的超时重试机制,它的重试策略是固定次数的重试,而这种策略在某些场景下效果并不理想。例如我们设置的重试次数为 3,此时因为网络短暂抖动造成了服务调用失败,而固定策略可能在网络恢复前就已经用完了所有的重试次数,这样就导致重试机制的作用不大。有时候我们更需要的是指数增长的重试策略,就像 TCP 的超时重传一样,那种指数增长的重试策略更加的智能,它会在每次重试失败后增加等待时间,给网络或者服务更多的恢复时间,并减少因短时间内多次重试对服务的潜在影响。

4.2 如何自定义超时重试机制

  1. 自定义超时重试类(实现 Retryer 接口,并重写 continueOrPropagate 方法)
  2. 设置配置文件

① 自定义超时重试类(指数增长)

/*** 自定义超时重试类*/
public class CustomRetryer implements Retryer {private final int maxAttempts;  // 最大尝试次数private final long backoff;  // 重试间隔时间int attempt; // 当前重试次数public CustomRetryer() {this.maxAttempts = 3;this.backoff = 1000L;this.attempt = 0;}@Overridepublic void continueOrPropagate(RetryableException e) {if(attempt++ >= maxAttempts) {throw e;}long interval = this.backoff;System.out.println(LocalDateTime.now() + " | 执行一次重试: " + interval);try {Thread.sleep(interval * attempt); // 指数增长} catch (InterruptedException ex) {throw new RuntimeException(ex);}}@Overridepublic Retryer clone() {return new CustomRetryer();}
}

② 设置配置文件

retryer 后面写上自定义超时重试类的包名+ 类名。

启动服务实例和消费者,并尝试获取服务,报错后,再查看控制台:

1. 此处我设置了最大重传次数为 3 打印 4 次日志,是因为我自定义的重试类的 attempt 变量从 0 开始的。

2. 观察日志与日志间的时间间隔:从 2s -> 3s -> 4s,最初 attempt 为 1,1*1 + read-timeout 的 1s 所以就是 2s,然后再试 1*2 + read-timeout,以此类推...

5. OpenFeign 超时重试的底层原理

想要搞懂 OpenFeign 超时重试的底层原理,就得先搞清楚 OpenFeign 的底层实现:

① 加注解

        在启动类或者配置类上添加 @EnableFeignClients注解
② 动态代理

        这个注解会触发Spring框架的自动配置机制,扫描所有标记有@FeignClient的接口,并为它们创建代理实例

③ RequestTemplate 发送HTTP请求

        此处的 RequeustTemplate 我们可以理解为 RestTemplate,因为他俩的目的相同。OpenFeign 不能直接发送 HTTP 请求,它在动态代理里面做了一件事,它将注解里面请求的路由地址拿出来,然后就能拼出来一个 URL 请求的地址,然后再使用 RequestTemplate(RestTemplate)去发送 HTTP 请求。

④ RestTemplate 依靠 HTTP 框架实现 web 请求 (把它理解为 RestTemplate)

        RestTemplate 只是一个模板方法类,它只是规定了一个调用的 API,它底层并没有实现,它依靠的是 HTTP 框架实现的 web 请求 (阿帕奇的 HttpClient 框架

5.1 超时重试原理

        所以我们的超时重试原理,就是在 HttpClient 里边设置超时时间,然后如果超时了,还没获取到服务列表,报错信息就会一步一步返回给上层,最终给到 OpenFeign。

想要在了解的细致一些,就可以去看看 OpenFeign 的底层源码:

public Object invoke(Object[] argv) throws Throwable {RequestTemplate template = this.buildTemplateFromArgs.create(argv);Request.Options options = this.findOptions(argv);Retryer retryer = this.retryer.clone();// 死循环,如果成功或者重试结束就返回 [通过throw终止while循环]while(true) {try {// 通过 Http Client 发起通信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 != Logger.Level.NONE) {this.logger.logRetry(this.metadata.configKey(), this.logLevel);}}}
}

源码中使用 RequestTemplate 发送 HTTP 请求,我们主要关注两个地方:

  1. this.executeAndDecode(template, options); // HttpClient 进行通信
  2. retryer.continueOrPropagate(e);  // 重试机制(默认 / 自定义)
所以,OpenFeign 的重试机制是通过其内置的 Retryer 组件和底层的 HTTP 客户端实现的。
Retryer 组件提供了重试策略的逻辑实现,而远程接口则通过 HTTP 客户端来完成调用!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯】2023省赛H题

考察知识点:双向链表,小根堆 完整代码在文章末尾 题目 【问题描述】 给定一个长度为 N 的整数数列: A1,A2,...,AN。你要重复以下操作 K 次 :…

下载安装各种版本的Vscode以及解决VScode官网下载慢的问题

下载指定版本 在Vscode官网 Vscode官网更新子页 这里的左侧栏点击其中一个会跳转到某个版本,或者在官网子页 https://code.visualstudio.com/updates的后面跟上需要的版本号即可完成目标版本下载页面的跳转 选择Linux里的ARM包不会自动下载而是跳转到另一个页面 …

【大模型应用开发教程】04_大模型开发整体流程 基于个人知识库的问答助手 项目流程架构解析

大模型开发整体流程 & 基于个人知识库的问答助手 项目流程架构解析 一、大模型开发整体流程1. 何为大模型开发定义核心点核心能力 2. 大模型开发的整体流程1. 设计2. 架构搭建3. Prompt Engineering4. 验证迭代5. 前后端搭建 二、项目流程简析步骤一:项目规划与…

Sync Folders Pro(文件夹数据同步工具)

Sync Folders Pro for Mac 是一款功能强大的文件夹同步工具,旨在帮助用户在 Mac 计算机和移动设备之间创建双向同步。这款软件支持各种文件系统和设备,如 iPhone,iPad,iPod,Android 等。通过这款软件,用户可…

【Spring Security】Spring Security 认证过程源码分析

项目启动 我们在前面有了解到可以在application.yml中配置用户名密码,那么可以猜想:肯定是在项目启动的时候加载的,我们通过鼠标点击 进入SecurityProperties,我们在User中的getName上打断点,这样项目启动的时候就可以走到这里 之后我们通过点击:Drop Frame可以往回走进…

基于白冠鸡算法的无人机航迹规划-附代码

基于白冠鸡算法的无人机航迹规划 文章目录 基于白冠鸡算法的无人机航迹规划1.白冠鸡搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用白冠鸡算法来优化无人机航迹规划。 1.白冠鸡…

Python爬取读书网的图片链接和书名并保存在数据库中

一个比较基础且常见的爬虫,写下来用于记录和巩固相关知识。 一、前置条件 本项目采用scrapy框架进行爬取,需要提前安装 pip install scrapy# 国内镜像 pip install scrapy -i https://pypi.douban.com/simple 由于需要保存数据到数据库,因…

安达发|APS生产排程解决五金制造企业的需求

在五金制造行业中,生产排程一直是一个非常重要的环节。然而,由于五金行业的特点和痛点,传统的生产排程方法往往难以满足企业的需求。本文将针对五金行业的痛点,探讨如何利用APS生产排程解决这些问题。 首先,我们需要了…

JAVA毕业设计109—基于Java+Springboot+Vue的宿舍管理系统(源码+数据库)

基于JavaSpringbootVue的宿舍管理系统(源码数据库)109 一、系统介绍 本系统前后端分离 本系统分为学生、宿管、超级管理员三种角色 1、用户: 登录、我的宿舍、申请调宿、报修申请、水电费管理、卫生检查、个人信息修改。 2、宿管: 登录、用户管理…

为机器学习算法准备数据(Machine Learning 研习之八)

本文还是同样建立在前两篇的基础之上的! 属性组合实验 希望前面的部分能让您了解探索数据并获得洞察力的几种方法。您发现了一些数据怪癖,您可能希望在将数据提供给机器学习算法之前对其进行清理,并且发现了属性之间有趣的相关性&#xff0c…

谈一谈SQLite、MySQL、PostgreSQL三大数据库

每一份付出,必将有一份收货,就像这个小小的果实,时间到了,也就会开花结果… 三大数据库概述 SQLite、MySQL 和 PostgreSQL 都是流行的关系型数据库管理系统(RDBMS),但它们在功能、适用场景和性…

Leetcode刷题详解——全排列

1. 题目链接:46. 全排列 2. 题目描述: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],…

pcigo图床插件的简单开发

1.前言: 如果想写一个图床并且投入使用,那么,接入picgo一定是一个不错的选择。picgo有着windows,mac,linux等多个客户端版本。实用且方便。 2. 开发的准备: 2.0. 需要安装一个node node这里我就不详细说…

网络攻击的威胁仍在上升、企业该如何解决

近十年来,网络攻击的频率和规模不断增加,对网站和在线资产构成了严重威胁。解决网站被攻击的问题需要采用多层次的安全策略,其中CDN(内容分发网络)的防护角度发挥了关键作用。本文将通过通俗易懂的方式从CDN的角度分析…

3.线性神经网络-3GPT版

#pic_center R 1 R_1 R1​ R 2 R^2 R2 目录 知识框架No.1 线性回归基础优化算法一、线性回归1、买房案例2、买房模型简化3、线性模型4、神经网络5、损失函数6、训练数据7、参数学习8、显示解9、总结 二、 基础优化算法1、梯度下降2、学习率3、小批量随机梯度下降4、批量大小5、…

jetsonTX2 nx配置yolov5和D435I相机,完整步骤

转载一篇问题解决博客:问题解决 一、烧录系统 使用SDK烧录 二、安装archiconda3 JETSON TX2 NX的架构是aarch64,与win10,linxu不同,所以不能安装Anaconda,这里安装对应的archiconda。 1. 安装 wget https://github.com/Archiconda/build-tools/rel…

ACM MM 2023 | 清华、华为联合提出MISSRec:兴趣感知的多模态序列推荐预训练

©PaperWeekly 原创 作者 | 王锦鹏 单位 | 清华大学深圳国际研究生院 研究方向 | 多模态检索、推荐系统 序列推荐是一种主流的推荐范式,目的是从用户的历史行为中推测用户偏好,并为之推荐感兴趣的物品。现有的大部分模型都是基于 ID 和类目等信息做…

如何搭建低成本亚马逊aws云服务器

0. 环境 win10 火狐浏览器 1. 登录 https://aws.amazon.com/cn/ -> 登录 -> 根用户 -> ********, **** 如果未有,需要注册,去年我注册的,麻烦之处是需要添加信用卡。可以淘宝aws搜索商家帮忙处理。 2. 控制台 在控制台主页&…

Leetcode41缺失的第一个正数

思路:原地哈希表 长度为N的数组,没有出现过的正整数一定是1~N1中的一个。 此时会思考能不能用一个哈希表来保存出现过的1~N1的数,然后从 1 开始依次枚举正整数,并判断其是否在哈希表中 但是题目要求常数级别的空间,就不…

VB.NET—窗体引起的乌龙事件

目录 前言: 过程: 总结: 升华: 前言: 分享一个VB.NET遇到的一个问题,开始一直没有解决,这个问题阻碍了很长时间,成功的变成我路上的绊脚石,千方百计的想要绕过去,但事与愿违怎么也绕不过去,因为运行不了…