聊聊HttpClient的重试机制
序
本文主要研究一下HttpClient的重试机制
HttpRequestRetryHandler
org/apache/http/client/HttpRequestRetryHandler.java
public interface HttpRequestRetryHandler {/*** Determines if a method should be retried after an IOException* occurs during execution.** @param exception the exception that occurred* @param executionCount the number of times this method has been* unsuccessfully executed* @param context the context for the request execution** @return {@code true} if the method should be retried, {@code false}* otherwise*/boolean retryRequest(IOException exception, int executionCount, HttpContext context);}
HttpRequestRetryHandler接口定义了retryRequest方法,它接收IOException、executionCount及context,然后判断是否可以重试
DefaultHttpRequestRetryHandler
org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler();/** the number of times a method will be retried */private final int retryCount;/** Whether or not methods that have successfully sent their request will be retried */private final boolean requestSentRetryEnabled;private final Set<Class<? extends IOException>> nonRetriableClasses;/*** Create the request retry handler using the specified IOException classes** @param retryCount how many times to retry; 0 means no retries* @param requestSentRetryEnabled true if it's OK to retry requests that have been sent* @param clazzes the IOException types that should not be retried* @since 4.3*/protected DefaultHttpRequestRetryHandler(final int retryCount,final boolean requestSentRetryEnabled,final Collection<Class<? extends IOException>> clazzes) {super();this.retryCount = retryCount;this.requestSentRetryEnabled = requestSentRetryEnabled;this.nonRetriableClasses = new HashSet<Class<? extends IOException>>();for (final Class<? extends IOException> clazz: clazzes) {this.nonRetriableClasses.add(clazz);}}/*** Create the request retry handler using the following list of* non-retriable IOException classes: <br>* <ul>* <li>InterruptedIOException</li>* <li>UnknownHostException</li>* <li>ConnectException</li>* <li>SSLException</li>* </ul>* @param retryCount how many times to retry; 0 means no retries* @param requestSentRetryEnabled true if it's OK to retry non-idempotent requests that have been sent*/@SuppressWarnings("unchecked")public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {this(retryCount, requestSentRetryEnabled, Arrays.asList(InterruptedIOException.class,UnknownHostException.class,ConnectException.class,SSLException.class));}/*** Create the request retry handler with a retry count of 3, requestSentRetryEnabled false* and using the following list of non-retriable IOException classes: <br>* <ul>* <li>InterruptedIOException</li>* <li>UnknownHostException</li>* <li>ConnectException</li>* <li>SSLException</li>* </ul>*/public DefaultHttpRequestRetryHandler() {this(3, false);}/*** Used {@code retryCount} and {@code requestSentRetryEnabled} to determine* if the given method should be retried.*/@Overridepublic boolean retryRequest(final IOException exception,final int executionCount,final HttpContext context) {Args.notNull(exception, "Exception parameter");Args.notNull(context, "HTTP context");if (executionCount > this.retryCount) {// Do not retry if over max retry countreturn false;}if (this.nonRetriableClasses.contains(exception.getClass())) {return false;}for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {if (rejectException.isInstance(exception)) {return false;}}final HttpClientContext clientContext = HttpClientContext.adapt(context);final HttpRequest request = clientContext.getRequest();if(requestIsAborted(request)){return false;}if (handleAsIdempotent(request)) {// Retry if the request is considered idempotentreturn true;}if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {// Retry if the request has not been sent fully or// if it's OK to retry methods that have been sentreturn true;}// otherwise do not retryreturn false;}/*** @return {@code true} if this handler will retry methods that have* successfully sent their request, {@code false} otherwise*/public boolean isRequestSentRetryEnabled() {return requestSentRetryEnabled;}/*** @return the maximum number of times a method will be retried*/public int getRetryCount() {return retryCount;}/*** @since 4.2*/protected boolean handleAsIdempotent(final HttpRequest request) {return !(request instanceof HttpEntityEnclosingRequest);}/*** @since 4.2** @deprecated (4.3)*/@Deprecatedprotected boolean requestIsAborted(final HttpRequest request) {HttpRequest req = request;if (request instanceof RequestWrapper) { // does not forward request to originalreq = ((RequestWrapper) request).getOriginal();}return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted());}}
DefaultHttpRequestRetryHandler实现了HttpRequestRetryHandler接口,其无参构造器默认将InterruptedIOException、UnknownHostException、ConnectException、SSLException设定为不重试的异常,默认retryCount为3,requestSentRetryEnabled为false;其retryRequest方法先判断executionCount是否超出retryCount,接着判断异常类型是否是不重试的异常类型,若request为aborted则返回false,若request非HttpEntityEnclosingRequest则表示幂等请求,返回true,若请求未完全发送则返回true,其余的默认返回false。
RetryExec
org/apache/http/impl/execchain/RetryExec.java
@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
public class RetryExec implements ClientExecChain {private final Log log = LogFactory.getLog(getClass());private final ClientExecChain requestExecutor;private final HttpRequestRetryHandler retryHandler;public RetryExec(final ClientExecChain requestExecutor,final HttpRequestRetryHandler retryHandler) {Args.notNull(requestExecutor, "HTTP request executor");Args.notNull(retryHandler, "HTTP request retry handler");this.requestExecutor = requestExecutor;this.retryHandler = retryHandler;}@Overridepublic CloseableHttpResponse execute(final HttpRoute route,final HttpRequestWrapper request,final HttpClientContext context,final HttpExecutionAware execAware) throws IOException, HttpException {Args.notNull(route, "HTTP route");Args.notNull(request, "HTTP request");Args.notNull(context, "HTTP context");final Header[] origheaders = request.getAllHeaders();for (int execCount = 1;; execCount++) {try {return this.requestExecutor.execute(route, request, context, execAware);} catch (final IOException ex) {if (execAware != null && execAware.isAborted()) {this.log.debug("Request has been aborted");throw ex;}if (retryHandler.retryRequest(ex, execCount, context)) {if (this.log.isInfoEnabled()) {this.log.info("I/O exception ("+ ex.getClass().getName() +") caught when processing request to "+ route +": "+ ex.getMessage());}if (this.log.isDebugEnabled()) {this.log.debug(ex.getMessage(), ex);}if (!RequestEntityProxy.isRepeatable(request)) {this.log.debug("Cannot retry non-repeatable request");throw new NonRepeatableRequestException("Cannot retry request " +"with a non-repeatable request entity", ex);}request.setHeaders(origheaders);if (this.log.isInfoEnabled()) {this.log.info("Retrying request to " + route);}} else {if (ex instanceof NoHttpResponseException) {final NoHttpResponseException updatedex = new NoHttpResponseException(route.getTargetHost().toHostString() + " failed to respond");updatedex.setStackTrace(ex.getStackTrace());throw updatedex;}throw ex;}}}}}
RetryExec实现了ClientExecChain接口,其execute方法会循环执行requestExecutor.execute,它catch了IOException,对于retryHandler.retryRequest(ex, execCount, context)返回true的,会在通过RequestEntityProxy.isRepeatable(request)判断一下是否是可重复读取的request,不是则抛出NonRepeatableRequestException;对于retryHandler.retryRequest返回false的则针对NoHttpResponseException重新包装一下,将targetHost体现在message里头然后重新抛出
HttpEntityEnclosingRequest
org/apache/http/HttpEntityEnclosingRequest.java
public interface HttpEntityEnclosingRequest extends HttpRequest {/*** Tells if this request should use the expect-continue handshake.* The expect continue handshake gives the server a chance to decide* whether to accept the entity enclosing request before the possibly* lengthy entity is sent across the wire.* @return true if the expect continue handshake should be used, false if* not.*/boolean expectContinue();/*** Associates the entity with this request.** @param entity the entity to send.*/void setEntity(HttpEntity entity);/*** Returns the entity associated with this request.** @return entity*/HttpEntity getEntity();}
HttpEntityEnclosingRequest定义了getEntity、setEntity、expectContinue方法,它的子类有HttpPut、HttpPost、HttpPatch、HttpDelete等
RequestEntityProxy.isRepeatable
org/apache/http/impl/execchain/RequestEntityProxy.java
class RequestEntityProxy implements HttpEntity {private final HttpEntity original;private boolean consumed = false;public boolean isConsumed() {return consumed;}static boolean isRepeatable(final HttpRequest request) {if (request instanceof HttpEntityEnclosingRequest) {final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();if (entity != null) {if (isEnhanced(entity)) {final RequestEntityProxy proxy = (RequestEntityProxy) entity;if (!proxy.isConsumed()) {return true;}}return entity.isRepeatable();}}return true;}
}
RequestEntityProxy提供了静态方法isRepeatable用于判断该request的entity是否可以重复读取,对于非HttpEntityEnclosingRequest的返回true,是HttpEntityEnclosingRequest类型的话则判断entity.isRepeatable(),若entity是RequestEntityProxy类型的,则通过RequestEntityProxy.isConsumed来判断
entity.isRepeatable()
public interface HttpEntity {/*** Tells if the entity is capable of producing its data more than once.* A repeatable entity's getContent() and writeTo(OutputStream) methods* can be called more than once whereas a non-repeatable entity's can not.* @return true if the entity is repeatable, false otherwise.*/boolean isRepeatable();//......
}
HttpEntity接口定义了isRepeatable方法,用于表示entity的content及OutputStream是否可以读写多次。其实现类里头,BufferedHttpEntity、ByteArrayEntity、EntityTemplate、FileEntity、SerializableEntity、StringEntity为true,BasicHttpEntity、InputStreamEntity、StreamingHttpEntity为false
小结
HttpRequestRetryHandler接口定义了retryRequest方法,它接收IOException、executionCount及context,然后判断是否可以重试
DefaultHttpRequestRetryHandler实现了HttpRequestRetryHandler接口,其无参构造器默认将InterruptedIOException、UnknownHostException、ConnectException、SSLException设定为不重试的异常,默认retryCount为3,requestSentRetryEnabled为false;其retryRequest方法先判断executionCount是否超出retryCount,接着判断异常类型是否是不重试的异常类型,若request为aborted则返回false,若request非HttpEntityEnclosingRequest则表示幂等请求,返回true,若请求未完全发送则返回true,其余的默认返回false。
RetryExec实现了ClientExecChain接口,其execute方法会循环执行requestExecutor.execute,它catch了IOException,对于retryHandler.retryRequest(ex, execCount, context)返回true的,会在通过RequestEntityProxy.isRepeatable(request)判断一下是否是可重复读取的request,不是则抛出NonRepeatableRequestException
DefaultHttpRequestRetryHandler针对不是幂等请求的HttpEntityEnclosingRequest类型(
HttpPut、HttpPost、HttpPatch、HttpDelete),不会重试;若retryHandler.retryRequest返回可以重试,RetryExec还有一个repeatable的判断,BufferedHttpEntity、ByteArrayEntity、EntityTemplate、FileEntity、SerializableEntity、StringEntity为true,BasicHttpEntity、InputStreamEntity、StreamingHttpEntity为false
相关文章:
聊聊HttpClient的重试机制
序 本文主要研究一下HttpClient的重试机制 HttpRequestRetryHandler org/apache/http/client/HttpRequestRetryHandler.java public interface HttpRequestRetryHandler {/*** Determines if a method should be retried after an IOException* occurs during execution.**…...
北邮22级信通院数电:Verilog-FPGA(4)第三周实验:按键消抖、呼吸灯、流水灯 操作流程注意事项
北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章,请访问专栏: 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.注意事项 二.按键消抖 2.1 LED_deboun…...
Ghidra101再入门(上?)-Ghidra架构介绍
Ghidra101再入门(上?)-Ghidra架构介绍 最近有群友问我,说:“用了很多年的IDA,最近想看看Ghidra,这应该怎么进行入门?“这可难到我了。。 我发现,市面上虽然介绍Ghidra怎么用的文章和书籍很多&…...
Vue3路由引入报错解决:无法找到模块“xxx.vue”的声明文件 xxx隐式拥有 “any“ 类型。
这类情况应该遇见过吧,这是因为 TypeScript只能理解 .ts 文件,无法理解 .vue 文件。 解决方法:在项目的根目录或者src文件夹下创建一个后辍为 文件名.d.ts 的文件,并写入一下内容: declare module *.vue {import { …...
基于若依ruoyi-nbcio支持flowable流程分类里增加流程应用类型
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 主要考虑到流程分很多种,普通的是OA流程,还有自定义业务流程,钉钉流程等…...
JS之同步异步promise、async、await
promise异步操作 Promise是异步编程的一种解决方案 JavaScript异步与同步解析 学习promise前我们先来了解下什么是异步? 基本概念: 消息队列中的任务分为宏任务与微任务;调用栈也可以称为主线程 首先我们要知道js是单线程语言,也就是说…...
【OpenCV • c++】自定义直方图 | 灰度直方图均衡 | 彩色直方图均衡
文章目录 一、什么是直方图二、自定义直方图三、灰度直方图均衡四、彩色直方图均衡一、什么是直方图 直方图广泛应用于很多计算机视觉处理当中。通过标记帧与帧之间显著的边缘和颜色的变化,可以检测视频中的场景变化。在每个兴趣点设置一个有相似特征的直方图所构成的“标签”…...
el-tree目录和el-table实现搜索定位高亮方法
需求:el-tree目录实现搜索查询el-table表格项,双击表格项根据yiZhuMLID||muLuID定位el-tree目录,并且高亮展示在可视化区域内,再重新根据el-tree目录的yiZhuMLID搜索刷新el-table表格,定位且高亮展示相对应的yiZhuMLID…...
linux常用指令
基础命令 cd:用于切换目录。例如,要从当前目录切换到/home/user目录,可以使用命令“cd /home/user”。ls:用于列出目录内容。例如,要列出当前目录的内容,可以使用命令“ls”。mkdir:用于创建目…...
C语言,指针的一些运算
若创建一个数组:int arr[10] 0; 用指针变量来储存数组首元素的地址:int* p arr,这里arr是数组名,表示首元素地址。 若p p 1或者p之后p本来指向数组首元素地址,就变成了指向第二个元素的地址,p n即指向第n 1个地…...
iPhone 如何强制重启
参考iPhone的官方使用手册 传送门 尤其当 iPhone 未响应,也无法将其关机再开机,此方法最有效: 按住调高音量按钮,然后快速松开。按住调低音量按钮,然后快速松开。按住侧边按钮。当 Apple 标志出现时,松开侧…...
数据结构--单链表操作
1.单链表的创建(带头结点) #include<stdlib.h> #define ElemType int typedef struct {//定义一个结点ElemType data;struct STU* next; }STU,*LinkList; bool InitList(LinkList& L) {L (STU*)malloc(sizeof(STU));//创建头结点if (L NUL…...
AlmaLinux (兼容centos)安装Geant4与ROOT
AlmaLinux 介绍 AlmaLinux OS 是一个开源、社区驱动的 Linux 操作系统,它填补了因 CentOS 稳定版本停止维护而留下的空白,同时更加强大。 安装 AlmaLinux 这个我用的是 windows 子系统进行安装 首先打开微软商店,然后搜索AlmaLinux&#…...
FPGA面试题(2)
一.同步复位和异步复位 同步复位:当clk有效时,复位才有效。优点:有利于时序分析,防止毛刺现象出现。缺点:复位信号必须大于时钟周期,大部分逻辑器件中D触发器都只有异步复位端口,需要在寄存器数…...
【C++ Primer Plus学习记录】指针——使用new来创建动态数组
目录 1.使用new创建动态数组 2.使用动态数组(如何使用指针访问数组元素) 如果程序只需要一个值,则可能会声明一个简单变量,因为对于管理一个小型数据对象来说,这样做比使用new和指针更简单。通常,对于大型…...
移动app广告变现,对接广告联盟还是选择第三方聚合广告平台?
作为互联网广告的载体,APP天生就比线下传统广告位更具优势,不受地域限制可以辐射到地球上的每一个角落,可以让广告获得更广的覆盖面。通过丰富的广告形式,精准的目标用户画像,也可以更好地实现品牌广告或效果广告的投放…...
ARM 按键控制 LED灯,蜂鸣器,风扇
main.c: #include "uart.h" #include "key_it.h" int main() {all_led_init();uart4_init();//串口初始化//中断初始化key_it_config();key3_it_config();buzzer_init();fan_init();while(1){//保证主程序不结束}return 0; }src/key_it.c: #include"…...
VirtualBox Ubuntu扩展虚拟机磁盘空间
关于Orical VM VirtualBox虚拟机安装了ubuntu linux系统,由于需要,磁盘空间不足,需要扩展磁盘空间,最终找到了一个非常简单的方法,上干货。 1、关闭虚拟机 2、运用VBoxManage命令扩展vdi文件的空间 打开windows的命…...
C#开发的OpenRA游戏之电力系统之二
C#开发的OpenRA游戏之电力系统之二 继续前面的电力系统分析,在OpenRA游戏里,每一个建筑物都会有一个电力描述字段,说明这个建筑物是消耗电力,还是产生电力的。如果这个建筑物是产生电力的,那么这个字段就会是正值,如果这个建筑物是消耗电力的,就会是负值。因此所有电厂…...
Java架构师基础框架设计
目录 1 导学2 理解软件框架3 框架设计里面的框架和设计模式的关系4 基础框架中常见的基本功能4.1 事务处理4.2 微服务网络调用4.3 缓存实现4.4 分布式id4.5 任务调度4.6 工作流5 基础框架的几种基本的使用方式5.1 继承方式5.2 注解或注解加AOP的方式5.3 将基础框架的功能直接当…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
