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

聊聊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信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.注意事项 二.按键消抖 2.1 LED_deboun…...

Ghidra101再入门(上?)-Ghidra架构介绍

Ghidra101再入门(上&#xff1f;)-Ghidra架构介绍 最近有群友问我&#xff0c;说&#xff1a;“用了很多年的IDA&#xff0c;最近想看看Ghidra&#xff0c;这应该怎么进行入门&#xff1f;“这可难到我了。。 我发现&#xff0c;市面上虽然介绍Ghidra怎么用的文章和书籍很多&…...

Vue3路由引入报错解决:无法找到模块“xxx.vue”的声明文件 xxx隐式拥有 “any“ 类型。

这类情况应该遇见过吧&#xff0c;这是因为 TypeScript只能理解 .ts 文件&#xff0c;无法理解 .vue 文件。 解决方法&#xff1a;在项目的根目录或者src文件夹下创建一个后辍为 文件名.d.ts 的文件&#xff0c;并写入一下内容&#xff1a; declare module *.vue {import { …...

基于若依ruoyi-nbcio支持flowable流程分类里增加流程应用类型

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 主要考虑到流程分很多种&#xff0c;普通的是OA流程&#xff0c;还有自定义业务流程&#xff0c;钉钉流程等…...

JS之同步异步promise、async、await

promise异步操作 Promise是异步编程的一种解决方案 JavaScript异步与同步解析 学习promise前我们先来了解下什么是异步&#xff1f; 基本概念&#xff1a; 消息队列中的任务分为宏任务与微任务;调用栈也可以称为主线程 首先我们要知道js是单线程语言&#xff0c;也就是说…...

【OpenCV • c++】自定义直方图 | 灰度直方图均衡 | 彩色直方图均衡

文章目录 一、什么是直方图二、自定义直方图三、灰度直方图均衡四、彩色直方图均衡一、什么是直方图 直方图广泛应用于很多计算机视觉处理当中。通过标记帧与帧之间显著的边缘和颜色的变化,可以检测视频中的场景变化。在每个兴趣点设置一个有相似特征的直方图所构成的“标签”…...

el-tree目录和el-table实现搜索定位高亮方法

需求&#xff1a;el-tree目录实现搜索查询el-table表格项&#xff0c;双击表格项根据yiZhuMLID||muLuID定位el-tree目录&#xff0c;并且高亮展示在可视化区域内&#xff0c;再重新根据el-tree目录的yiZhuMLID搜索刷新el-table表格&#xff0c;定位且高亮展示相对应的yiZhuMLID…...

linux常用指令

基础命令 cd&#xff1a;用于切换目录。例如&#xff0c;要从当前目录切换到/home/user目录&#xff0c;可以使用命令“cd /home/user”。ls&#xff1a;用于列出目录内容。例如&#xff0c;要列出当前目录的内容&#xff0c;可以使用命令“ls”。mkdir&#xff1a;用于创建目…...

C语言,指针的一些运算

若创建一个数组&#xff1a;int arr[10] 0; 用指针变量来储存数组首元素的地址&#xff1a;int* p arr,这里arr是数组名&#xff0c;表示首元素地址。 若p p 1或者p之后p本来指向数组首元素地址&#xff0c;就变成了指向第二个元素的地址&#xff0c;p n即指向第n 1个地…...

iPhone 如何强制重启

参考iPhone的官方使用手册 传送门 尤其当 iPhone 未响应&#xff0c;也无法将其关机再开机&#xff0c;此方法最有效&#xff1a; 按住调高音量按钮&#xff0c;然后快速松开。按住调低音量按钮&#xff0c;然后快速松开。按住侧边按钮。当 Apple 标志出现时&#xff0c;松开侧…...

数据结构--单链表操作

1.单链表的创建&#xff08;带头结点&#xff09; #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 操作系统&#xff0c;它填补了因 CentOS 稳定版本停止维护而留下的空白&#xff0c;同时更加强大。 安装 AlmaLinux 这个我用的是 windows 子系统进行安装 首先打开微软商店&#xff0c;然后搜索AlmaLinux&#…...

FPGA面试题(2)

一.同步复位和异步复位 同步复位&#xff1a;当clk有效时&#xff0c;复位才有效。优点&#xff1a;有利于时序分析&#xff0c;防止毛刺现象出现。缺点&#xff1a;复位信号必须大于时钟周期&#xff0c;大部分逻辑器件中D触发器都只有异步复位端口&#xff0c;需要在寄存器数…...

【C++ Primer Plus学习记录】指针——使用new来创建动态数组

目录 1.使用new创建动态数组 2.使用动态数组&#xff08;如何使用指针访问数组元素&#xff09; 如果程序只需要一个值&#xff0c;则可能会声明一个简单变量&#xff0c;因为对于管理一个小型数据对象来说&#xff0c;这样做比使用new和指针更简单。通常&#xff0c;对于大型…...

移动app广告变现,对接广告联盟还是选择第三方聚合广告平台?

作为互联网广告的载体&#xff0c;APP天生就比线下传统广告位更具优势&#xff0c;不受地域限制可以辐射到地球上的每一个角落&#xff0c;可以让广告获得更广的覆盖面。通过丰富的广告形式&#xff0c;精准的目标用户画像&#xff0c;也可以更好地实现品牌广告或效果广告的投放…...

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系统&#xff0c;由于需要&#xff0c;磁盘空间不足&#xff0c;需要扩展磁盘空间&#xff0c;最终找到了一个非常简单的方法&#xff0c;上干货。 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 将基础框架的功能直接当…...

MySQL InnoDB 内部架构详解

MySQL作为最流行的开源关系型数据库&#xff0c;其InnoDB存储引擎凭借事务支持、行级锁等特性成为企业级应用的首选。本文将深入解析InnoDB的核心架构设计&#xff0c;揭开其高性能与高可靠背后的实现奥秘。内存结构与缓冲机制InnoDB通过精巧的内存设计提升性能。缓冲池(Buffer…...

别再用老方法了!STM32CubeMX V6.x 保姆级配置流程,从选型到生成代码一气呵成

STM32CubeMX V6.x 高效开发指南&#xff1a;从零构建现代化嵌入式工程 第一次接触STM32CubeMX时&#xff0c;我还在用寄存器手动配置GPIO。当看到同事在十分钟内完成了一个带FreeRTOS和三个串口的项目框架时&#xff0c;那种震撼至今难忘。如今&#xff0c;CubeMX已经成为STM32…...

告别命令行恐惧:给Windows用户的银河麒麟V10服务器终端入门与VNC可视化管理指南

告别命令行恐惧&#xff1a;给Windows用户的银河麒麟V10服务器终端入门与VNC可视化管理指南 第一次面对纯黑底的终端窗口时&#xff0c;手指悬在键盘上方却不知该输入什么——这可能是许多Windows开发者切换到银河麒麟V10服务器操作系统时的真实写照。作为国产化浪潮中的主流服…...

EagleEye应用解析:DAMO-YOLO TinyNAS在工业质检中的实战案例

EagleEye应用解析&#xff1a;DAMO-YOLO TinyNAS在工业质检中的实战案例 1. 工业质检的挑战与解决方案 在现代化生产线上&#xff0c;产品质量检测一直是关键环节。传统人工质检面临诸多痛点&#xff1a;检测速度慢&#xff08;平均每个产品需要3-5秒&#xff09;、人力成本高…...

DeepSeek-R1 1.5B使用技巧:这样提问,AI回答更准确更详细

DeepSeek-R1 1.5B使用技巧&#xff1a;这样提问&#xff0c;AI回答更准确更详细 1. 引言&#xff1a;提问方式决定回答质量 你是否遇到过这样的情况&#xff1a;向AI提问后&#xff0c;得到的回答要么过于简略&#xff0c;要么偏离主题&#xff1f;这很可能不是模型能力的问题…...

华硕天选2 FA506Q FA706Q 原厂Win11 22H2系统分享下载-宇程系统站

华硕天选2 FA506Q和FA706Q系列笔记本自带一键恢复功能&#xff0c;即使系统出现问题或更换硬盘后也能通过原厂工厂文件恢复到出厂状态。支持多种型号如FA506QR、FA506QM等&#xff0c;用户可轻松恢复Windows 11 22H2家庭版系统及隐藏的恢复分区&#xff0c;确保设备稳定运行。 …...

S32K144外部中断实战:用按键控制LED,手把手教你避开中断标志位清除的坑

S32K144外部中断实战&#xff1a;从按键消抖到标志位管理的完整解决方案 在嵌入式开发中&#xff0c;外部中断是实现实时响应的关键机制。S32K144作为NXP面向汽车电子和工业控制的主力MCU&#xff0c;其中断系统的灵活性和可靠性备受开发者青睐。但看似简单的按键中断控制LED背…...

模拟增益与数字增益:曝光调整中的优先级策略与实战技巧

1. 模拟增益与数字增益的核心差异 第一次接触相机参数时&#xff0c;我也被这两个专业术语绕晕过。直到有次拍摄星空&#xff0c;发现ISO调到6400后画面全是彩色噪点&#xff0c;才真正理解它们的区别。简单来说&#xff0c;模拟增益就像在菜市场买菜时用放大镜看秤——在信号变…...

【教育部-工信部联合验证】:2026奇点大会认证的3个多模态教育OS底层协议,2025Q4起将成为智慧教育装备强制接入标准

第一章&#xff1a;2026奇点智能技术大会&#xff1a;多模态教育应用 2026奇点智能技术大会(https://ml-summit.org) 多模态教育引擎的核心架构 本届大会首次发布开源教育大模型框架 EduMultimodal-1.0&#xff0c;支持文本、手写笔迹、语音指令、课堂视频流及AR交互数据的联…...

IP地址到底是什么?一张图看懂+命令行/网站查询实操

在调试网络、配置服务器或排查登录风控时&#xff0c;几乎每天都要面对IP地址。但如果你问身边的人“IP到底是什么”&#xff0c;可能很多人都会一愣——能说出全称“互联网协议地址”的人不少&#xff0c;但真正理解它是什么、怎么用、如何快速查到自己的IP&#xff0c;还真不…...