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

记一次Apache HTTP Client问题排查

现象

通过日志查看,存在两种异常情况。
第一种:开始的时候HTTP请求会报超时异常。

762663363 [2023-07-21 06:04:25] [executor-64] ERROR - com.xxl.CucmTool - CucmTool|sendRisPortSoap error,url:https://xxxxxx/realtimeservice/services/RisPort
org.apache.http.conn.HttpHostConnectException: Connect to xxx [/xxx] failed: 连接超时

第二种:突然没有新的HTTP请求日志了,现象就是HTTP请求后,一直卡主,等待响应。

HTTP Client代码

先查看一下HTTP的请求代码
HTTP Client设置

private static CloseableHttpClient getHttpClient() {SSLContextBuilder builder = new SSLContextBuilder();CloseableHttpClient httpClient = null;try {builder.loadTrustMaterial(null, new TrustStrategy() {@Overridepublic boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {return true;}});SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();} catch (Exception e) {log.error("getHttpClient error:{}", e.getMessage(), e);}return httpClient;
}

请求方式(未设置http connection timeout 和 socket timeout)

HttpPost httpPost = new HttpPost(url);
try(CloseableHttpResponse response = getHttpClient().execute(httpPost)) {HttpEntity httpEntity = response.getEntity();if (httpEntity != null) {System.out.println(EntityUtils.toString(httpEntity, "UTF-8"));}
} catch (Exception e) {log.error("test,url:" + url, e);
}

进一步分析

连接超时

通过本地debug先找到了socket连接处的代码,如下所示。
socket连接请求在java.net.Socket#connect(java.net.SocketAddress, int)这里

image.png

可以看到如果不设置connect timeout,在java层面默认是无限超时,那实际是要受系统层面影响的。我们都知道TCP建立连接的第一步是发送syn,实际这一步系统层面会有一些控制。

Linux环境

linux下通过net.ipv4.tcp_syn_retries控制sync的超时情况

Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 127. Default value is 6, which corresponds to 63seconds till the last retransmission with the current initial RTO of 1second. With this the final timeout for an active TCP connection attempt will happen after 127seconds.

默认重试次数为6次,重试的间隔时间从1s开始每次都翻倍,6次的重试时间间隔为1s, 2s, 4s, 8s, 16s,32s, 总共63s,第6次发出后还要等64s都知道第6次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s + 64 = 127 s,TCP才会把断开这个连接。
第 1 次发送 SYN 报文后等待 1s(2 的 0 次幂),如果超时,则重试
第 2 次发送后等待 2s(2 的 1 次幂),如果超时,则重试
第 3 次发送后等待 4s(2 的 2 次幂),如果超时,则重试
第 4 次发送后等待 8s(2 的 3 次幂),如果超时,则重试
第 5 次发送后等待 16s(2 的 4 次幂),如果超时,则重试
第 6 次发送后等待 32s(2 的 5 次幂),如果超时,则重试
第 7 次发送后等待 64s(2 的 6 次幂),如果超时,则断开这个连接。

mac环境

mac场景下是通过net.inet.tcp.keepinit参数控制syn超时(默认是75s)。
可以通过下面的命令查看

sysctl -A |grep net.inet.tcp.keepinit

net.inet.tcp.keepinit: 75000
通过telnet验证,确实是75s超时

image.png
tcpdump抓包也可以看到一直进行syn重试。

image.png

读取超时

Apache Http Client 默认的socket read timeout 是0。
image.png
image.png
通过代码注释可以看到,如果soTimeout是0的话,就意味着读取超时不受限制,但是实际上这里也会有系统层面的控制,下面从HTTP层面和TCP层面做一下分析。

HTTP Keep-alive

首先,Apache httpClient 4.3版本中,如果请求中未做制定,那么默认会使用HTTP 1.1,代码如下。

public static ProtocolVersion getVersion(HttpParams params) {Args.notNull(params, "HTTP parameters");Object param = params.getParameter("http.protocol.version");return (ProtocolVersion)(param == null ? HttpVersion.HTTP_1_1 : (ProtocolVersion)param);
}

对于HTTP 1.1版本来说,默认会开启Keep-alive,并使用默认的keep-alive策略。

public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy();public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {Args.notNull(response, "HTTP response");final HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));while (it.hasNext()) {final HeaderElement he = it.nextElement();final String param = he.getName();final String value = he.getValue();if (value != null && param.equalsIgnoreCase("timeout")) {try {return Long.parseLong(value) * 1000;} catch(final NumberFormatException ignore) {}}}return -1;}}

其基本原理就是HTTP场景下,当客户端与服务端建立TCP连接以后,Httpd守护进程会通过keep-alive timeout时间设置参数,一个http产生的tcp连接在传送完最后一个响应后,如果守护进程在这个keepalive_timeout里,一直没有收到浏览器发过来http请求,则关闭这个http连接。
这里有两点要注意:

  • 可以看到keep-alive的超时时间是服务端返回时,http client在响应头中解析到的。如果一直未收到服务端响应,那么客户端会认为keep-alive一直有效;-1的返回值也是如此。
  • 如果服务端有响应,如果服务端有响应,那么就会按照服务端的返回设置keep-alive的timeout,当timeout到期后,就会从http client pool中移除,服务端关闭该TCP连接。

下面是一个成功的HTTP client响应信息,可以看到服务端给出的keep-alive时间是60s。

image.png
image.png

后续对于这个连接不做任何处理,可以看到60s以后断开了连接。

image.png

TCP下的keep-alive机制

TCP连接建立之后,如果某一方一直不发送数据,或者隔很长时间才发送一次数据,当连接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,连接还需不需要保持,这种情况在TCP协议设计中keep-alive的目的。
TCP协议中,当超过一段时间之后,TCP自动发送一个数据为空的报文(侦测包)给对方,如果对方回应了这个报文,说明对方还在线,连接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为连接丢失,没有必要保持连接。
在Linux系统中有以下配置用于TCP的keep-alive。


tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。

也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。

在MAC下对应的配置如下(单位为ms)


net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.keepcnt: 3

也就是说在Mac系统中,最少经过2小时3分钟45秒才可以发现一个「死亡」连接。

对于TCP的keep-alive是默认关闭的,可以通过应用层面打开。
对于Java应用程序,默认是关闭的,后面我们模拟在客户端开启该配置。

public static final SocketOption SO_KEEPALIVE
Keep connection alive.
The value of this socket option is a Boolean that represents whether the option is enabled or disabled. When the SO_KEEPALIVE option is enabled the operating system may use a keep-alive mechanism to periodically probe the other end of a connection when the connection is otherwise idle. The exact semantics of the keep alive mechanism is system dependent and therefore unspecified.
The initial value of this socket option is FALSE. The socket option may be enabled or disabled at any time.

首先,修改mac的keep-alive设置,将时间调短一些。

sysctl -w net.inet.tcp.keepidle=60000 net.inet.tcp.keepcnt=3 net.inet.tcp.keepintvl=10000 

net.inet.tcp.keepidle: 60000
net.inet.tcp.keepintvl: 10000
net.inet.tcp.keepcnt: 3

依然通过HTTP Client开启keep alive配置

SocketConfig socketConfig = SocketConfig.DEFAULT;
SocketConfig keepAliveConfig = SocketConfig.copy(socketConfig).setSoKeepAlive(true).build();
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultSocketConfig(keepAliveConfig).build();

通过HTTP Client请求服务端一个耗时很长的接口,并通过TCP抓包可以看到以下内容
每隔60s,客户端会向服务端发送保活的连接。

image.png
再来验证一下,如果服务端此时不可用的情况。
使用pfctl工具,模拟服务端不可达。
可以看到客户端每隔10s,累计尝试3次,然后就会关闭该连接。

image.png

image.png

image.png

回归问题

连接超时问题

此时服务器因为个别原因,无法正常连接。
由于HTTP Client未设置对应的超时时间,所以会依据系统的net.ipv4.tcp_syn_retries进行重试。
该异常客户端可以感知到。

请求卡主问题

当某个时间HTTP Client与服务器建立的正常的TCP连接后,服务器发生了异常,此时由于以下原因叠加

  • HTTP Client未设置socket读取超时时间
  • HTTP keep-alive也由于服务端未响应默认不受限制
  • 另外TCP层面的keep alive也没有手动开启

所以此时客户端会一直持有该TCP连接等待服务器响应。对应到下图的话,也就是橙色部分。

image.png
当然最直接的解决方案就是设置socket read timeout时间即可。

RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(1000).setSocketTimeout(1000).setConnectTimeout(1000).build();
httpPost.setConfig(requestConfig);

当时间到了会报read timeout 异常。

image.png

总结

  • 当我们使用HTTP Client的时候,需要结合业务需要合理设置connect timeout和 socket timeout参数。
  • 当进行问题追踪时,需要利用HTTP和TCP的一些知识,以及tcpdump等抓包工具进行问题验证。

参考文档

【1】 一文说清楚 Linux TCP 内核参数_linux tcp参数_DBA大董的博客-CSDN博客
【2】 服务端挂了,客户端的 TCP 连接还在吗?
【3】JAVA socket keep alive 说明https://docs.oracle.com/javase/8/docs/api/java/net/StandardSocketOptions.html#SO_KEEPALIVE

相关文章:

记一次Apache HTTP Client问题排查

现象 通过日志查看,存在两种异常情况。第一种:开始的时候HTTP请求会报超时异常。 762663363 [2023-07-21 06:04:25] [executor-64] ERROR - com.xxl.CucmTool - CucmTool|sendRisPortSoap error,url:https://xxxxxx/realtimeservice/services/RisPort o…...

Linux获取文件属性

以-rw-rw-r-- 1 ubuntu ubuntu 56 八月 1 19:37 1.txt 为例 一、stat函数 功能&#xff1a;获取文件的属性 函数原型&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>int stat(const char *pathname, struct stat *stat…...

String字符串拼接

String字符串拼接 1.简介2.StringBuilder2.1StringBuilder介绍2.2使用说明 3.StringBuffer4.StringJoiner5.String.Join() 1.简介 对于String来说是不可变的&#xff0c;使用修改字符串是在不断地创建新的字符串对象&#xff0c;而不是在原有的对象上修改的。并且对于字符串的…...

在矩池云使用Llama2-7B的具体方法

今天给大家分享如何在矩池云服务器使用 Llama2-7b模型。 硬件要求 矩池云已经配置好了 Llama 2 Web UI 环境&#xff0c;显存需要大于 8G&#xff0c;可以选择 A4000、P100、3090 以及更高配置的等显卡。 租用机器 在矩池云主机市场&#xff1a;https://matpool.com/host-m…...

API教程:轻松上手HTTP代理服务!

作为HTTP代理产品供应商&#xff0c;我们为您带来一份详细的教程&#xff0c;帮助您轻松上手使用API&#xff0c;并充分利用HTTP代理服务。无论您是开发人员、网络管理员还是普通用户&#xff0c;本教程将为您提供操作指南和代码模板&#xff0c;确保您能够顺利使用API并享受HT…...

脑网络通信:概念、模型与应用——Brain network communication: concepts, models and applications

脑网络通信:概念、模型与应用 介绍神经系统是通信网络从图论到大脑网络通信大脑网络通信模型和测量的分类法路由协议最短路径路由导航扩散过程广播(可通信性)参数模型线性阈值模型偏向性随机游走最短路径集合当前和新兴的应用将大脑结构与功能关联起来认知和临床表型的个体间…...

Docker创建tomcat容器实例后无法访问(HTTP状态 404 - 未找到)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…...

oracle数据库dbLink的使用

Oracle的数据库链路&#xff08;dbLink&#xff09;是一种允许在两个不同的数据库实例之间进行通信和数据交换的功能。它可以让你在一个数据库中访问另一个数据库的对象和数据&#xff0c;就像它们属于同一个数据库一样。 创建一个link: CREATE public DATABASE LINK link_sco…...

Coremail中睿天下|2023年第二季度企业邮箱安全态势观察

7月24日&#xff0c;Coremail邮件安全联合中睿天下发布《2023第二季度企业邮箱安全性研究报告》&#xff0c;对2023第二季度和2023上半年的企业邮箱的安全风险进行了分析。 一、垃圾邮件同比下降16.38% 根据Coremail邮件安全人工智能实验室&#xff08;以下简称AI实验室&#…...

ZooKeeper分布式锁、配置管理、服务发现在Java开发中的应用

ZooKeeper提供了多种功能&#xff0c;包括分布式锁、配置管理、服务发现、领导选举等。 下面是一些常见的ZooKeeper功能及其在Java中的应用示例代码。 分布式锁 import org.apache.zookeeper.*; import java.io.IOException; import java.util.concurrent.CountDownLatch;pu…...

openGauss学习笔记-27 openGauss 高级数据管理- JOIN

文章目录 openGauss学习笔记-27 openGauss 高级数据管理- JOIN27.1 交叉连接27.2 内连接27.3 左外连接27.4 右外连接27.5 全外连接 openGauss学习笔记-27 openGauss 高级数据管理- JOIN JOIN子句用于把来自两个或多个表的行结合起来&#xff0c;基于这些表之间的共同字段。 在…...

域名解析优先级

浏览器访问过程解析 访问网址——>首先在本地电脑看看hosts里面是否有域名对应IP地址&#xff0c;如何有直接访问对应IP&#xff0c; 如果没有&#xff0c;则联网询问DNS服务器&#xff08;一般网卡那边都配置了DNS服务器IP&#xff09; linux hosts 路径&#xff1a; w…...

【Opencv】视频跟踪算法KCF

目录 KCF算法简介opencv实现代码copencv实现代码python KCF算法简介 KCF&#xff08;Kernelized Correlation Filter&#xff09;是一种基于核相关滤波器的目标跟踪算法。它通过学习目标的外观特征和使用核相关滤波器进行目标定位。KCF属于传统算法的单目标跟踪器。下面是对KC…...

后端整理(集合框架、IO流、多线程)

1. 集合框架 Java集合类主要有两个根接口Collection和Map派生出来 Collection派生两个子接口 List List代表了有序可重复集合&#xff0c;可以直接根据元素的索引进行访问Set Set代表无序不可重复集合&#xff0c;只能根据元素本身进行访问 Map接口派生 Map代表的是存储key…...

C++ 类和对象篇(二) this指针

目录 一、this指针概念 二、this指针的特性 三、this指针存在哪里&#xff1f; 四、this指针可以为空吗&#xff1f; 一、this指针概念 1.是什么&#xff1f; 它是类内非静态成员函数的隐含形参&#xff0c;this指针指向调用该函数的对象。 this指针是C编译器给每个“非静态…...

Excel快捷键F1-F9详解:掌握实用快捷操作,提升工作效率

Excel是广泛应用于办公场景的优质电子表格软件&#xff0c;然而&#xff0c;许多人只是使用鼠标点击菜单和工具栏来完成操作&#xff0c;而忽略了快捷键的威力。在本文中&#xff0c;我们将详解Excel中的F1-F9快捷键&#xff0c;帮助您掌握实用的快捷操作&#xff0c;提升工作效…...

Webpack 安装教程

Webpack 是一个前端资源加载/打包工具。 安装 Webpack 使用 cnpm 安装 webpack&#xff1a; cnpm install webpack -g 创建项目 接下来我们创建一个目录 app&#xff1a; mkdir app 在 app 目录下添加 runoob1.js 文件&#xff0c;代码如下&#xff1a; app/runoob1.js 文件…...

移远通信首批加入“5G+eSIM计算终端产业合作计划”,助力大屏移动终端全时在线

7月29日&#xff0c;在全球数字娱乐产业盛会 ChinaJoy上&#xff0c;中国联通携手高通公司、GSMA发布了“5GeSIM 计算终端产业合作计划”。 作为全球领先的物联网整体解决方案供应商&#xff0c;移远通信首批加入该计划&#xff0c;副总经理刘明辉受邀参加5GeSIM 计算终端产业合…...

全网最强大的工具箱—utools介绍及分享

今天来介绍一个相见恨晚的PC端工具——utools&#xff0c;什么是utools&#xff1f;用其自身的话来说&#xff1a;“uTools是一个极简、插件化、跨平台的现代化桌面软件。通过自由选配丰富的插件&#xff0c;打造你得心应手的工具集合。”,体验了下&#xff0c;好用且强大&…...

Linux常用基础命令❀

文章目录❀ ❀ls命令 ❀cd命令 ❀pwd命令 ❀date命令 ❀创建、删除文件和目录命令 ❀alias命令 ❀复制、移动、重命名、查看&#xff08;文件、目录&#xff09;命令 ❀find查找、wc统计命令 ❀vi/vim命令 1、打开文件 2、工作模式 vi与vim的四个模式 进入编辑模式…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...