当前位置: 首页 > news >正文

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 客户端来完成调用!

相关文章:

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

目录 1. 什么是 OpenFeign? 2. OpenFeign 的功能升级 3. OpenFeign 内置的超时重试机制 3.1 配置超时重试 3.2 覆盖 Retryer 对象 4. 自定义超时重试机制 4.1 为什么需要自定义超时重试机制 4.2 如何自定义超时重试机制 5. OpenFeign 超时重试的底层原理 5…...

redis安装

redis安装 mac下直接安装 mac下安装redis还是很简单的(其实mac下安装什么软件都挺简单的,brew啥都有) brew install redis 之后就是漫长的等待,下了好久,终于下载完了 修改redis.conf中的配置 # 后台启动daemonize yes 启动服务端 redis-serv…...

VM虚拟机逆向 --- [NCTF 2018]wcyvm 复现

文章目录 前言题目分析 前言 第四题了,搞定,算是独立完成比较多的一题,虽然在还原汇编的时候还是很多问题。 题目分析 代码很简单,就是指令很多。 opcode在unk_6021C0处,解密的数据在dword_6020A0处 opcode [0x08, …...

2024天津理工大学中环信息学院专升本机械设计制造自动化专业考纲

2024年天津理工大学中环信息学院高职升本科《机械设计制造及其自动化》专业课考试大纲《机械设计》《机械制图》 《机械设计》考试大纲 教 材:《机械设计》(第十版),高等教育出版社,濮良贵、陈国定、吴立言主编&#…...

华为OD机试 - 服务失效判断 - 逻辑分析(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题&a…...

刚入职因为粗心大意,把事情办砸了,十分后悔

刚入职,就踩大坑,相信有很多朋友有我类似的经历。 5年前,我入职一家在线教育公司,新的公司福利非常好,各种零食随便吃,据说还能正点下班,一切都超出我的期望,“可算让我找着神仙公司…...

Docker学习——③

文章目录 1、Docker Registry(镜像仓库)1.1 什么是 Docker Registry?1.2 镜像仓库分类1.3 镜像仓库工作机制1.4 常用的镜像仓库 2、镜像仓库命令3、镜像命令[部分]4、容器命令[部分]4.1 docker run4.2 docker ps 5、CentOS 搭建一个 nginx 服…...

EMC Unity存储系统如何查看SSD的使用寿命

为什么要写这个博客? 客户对老的EMC unity的存储系统要扩容,如何确定SSD磁盘是全新的还是拆机二手的?很多时候客户还有一个奇葩的要求,就是要和5年前的磁盘PN一致,甚至要求固件版本一致,最关键的还要求是全…...

python创建一个简单的flask应用

下面用python在本地和服务器上分别创建一个简单的flask应用: 1.在pc本地 1)pip flask后创建一个简单的脚本flask_demo.py from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!winR进入命令行,…...

阿里云域名实战

一、准备阿里云服务器,实现网站功能 (1)百度搜索阿里云 (2)登录阿里云 可以使用支付宝,淘宝账号登录 (3)点击控制台 (4)创建实例,购买云服务器 (5&#x…...

git关联远程仓库自己分支自用

初始化仓库 cassielDESKTOP-KPKFOEU MINGW64 /d/code/api_test_202310232022 (tong) $ git init Reinitialized existing Git repository in D:/code/api_test_202310232022/.git/关联远程仓库并创建本地分支 cassielDESKTOP-KPKFOEU MINGW64 /d/code/api_test_202310232022 …...

eBPF BCC开源工具简介

目录 官方链接 编译安装 ubuntu版本 安装 examples tools hello_world.py demo 运行报错 网上目前的解决办法 错误分析过程 python版本检测 libbcc库检查 python3 bcc库检查 正常输出 监控进程切换 运行输出 监控CPU直方图 缓存命中率监控:caches…...

Linux上后台运行进程(nohub、screen和tmux )

文章目录 Linux上后台运行进程(nohub、screen和tmux )nohupscreen虚拟终端安装screen使用 tmux终端复用器[个人推荐]安装tmux使用 Linux上后台运行进程(nohub、screen和tmux ) 命令行的典型使用方式是,打开一个终端窗…...

javaee实验:搭建maven+spring boot开发环境,开发“Hello,Spring Boot”应用

目录 mavenspringboot实验目的实验内容环境的搭建 在开发中,maven和spring都是非常常用、非常重要的管理工具和框架,今天就在这里使用idea进行环境的搭建和创建第一个spring程序 maven 1.1maven是一个跨平台的项目管理工具(主要管理jar包&am…...

重新思考边缘负载均衡

本文介绍了Netflix在基于轮询的负载均衡的基础上,集成了包括服务器使用率在内的多因素指标,并对冷启动服务器进行了特殊处理,从而优化了负载均衡逻辑,提升了整体业务性能。原文: Rethinking Netflix’s Edge Load Balancing[1] 我…...

构建一个CAN报文周期任务类

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、can周期任务类构建总结 前言 提示:这里可以添加本文要记录的大概内容: 最近想着有时间实现总线报文收发的动态的配置,…...

深入理解计算机系统CS213 - Lecture 02

Bits, Bytes, and Integer 1.位运算与条件运算 &&#xff0c;|&#xff0c;^&#xff0c;~ 是做位运算。诸位01运算。 &&&#xff0c;||&#xff0c;&#xff01;是判断条件真假&#xff0c;而后返回0或1。 2. 位移 x << y&#xff1a;左移y位&#xff…...

【KVM】KVM介绍及功能概述

前言 大家好&#xff0c;我是秋意零。 今天介绍的内容是KVM的概述&#xff0c;以及它所支持的基本功能。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平台同名&#xff0c; 秋意零 账号创作者、 云社区 创建者&#x1f…...

centos7安装MySQL

引言 在安装与卸载的时候&#xff0c;用户全部要切换为root 在使用MySQL的时候&#xff0c;尽量使用root权限&#xff0c;便于使用&#xff08;root权限不会开的可以去百度找一下自己服务器类型的开启方式&#xff09;在本文章我将使用xshell7进行操作&#xff0c;你也可以使用…...

leetcode做题笔记215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5,6,4], k 2…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...