当前位置: 首页 > 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 将基础框架的功能直接当…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

Unity VR/MR开发-VR开发与传统3D开发的差异

视频讲解链接&#xff1a;【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...