记一次NoHttpResponseException问题排查
上传文件程序会有一定的概率提示错误,错误率大概在1%以下,错误信息是:org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws.com:80 failed to respond,看着是上传到S3的过程中发送了网络错误?

通过查阅资料,发现了一篇比较好的文章:一次NoHttpResponseException问题分析解决。这个文章的观点是会发生这个错误的原因是服务端关闭了连接,而客户端还在使用该连接,导致服务端响应RST报文,客户端收到RST报NoHttpResponseException异常。
为了说明这个场景,就要提一下Keepalive机制。Keepalive是HTTP的连接复用机制,在HTTP1.0时代,每个请求经过三次握手后,只会传输一次HTTP请求和响应报文后,就进入四次挥手关闭连接了。而TCP建立连接和关闭连接的代价是比较大的,导致HTTP1.0的通道利用率较低,时延较高。针对这个问题,退出了Keepalive机制,一个TCP连接建立后,可以在上面发送多个HTTP报文,只有这个TCP连接的空闲时间达到超时时间,才会被关闭。HTTP1.1默认开启Keepalive。这里的关闭行为可能发生在客户端和服务端,比如客户端的Keepalive超时时间更短,则客户端就会先关闭连接,如果服务端配置的Keepalive超时时间更短,则服务端就会先关闭连接。
乍看起来无论那一边关闭连接都没什么问题,但是还是有细节需要注意。比如服务端关闭连接,发送FIN包,在这个FIN包发送但是还未到达客户端期间,客户端如果继续复用这个TCP连接,发送HTTP请求报文的话,服务端会因为在四次挥手期间不接收报文而发送RST报文给客户端,客户端收到RST报文就会提示异常。
根据上面的理论知识,可以推测org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws.com:80 failed to respond这个错误发生的原因是因为我的程序的HttpClient的Keepalive时间大于S3服务器的,导致S3服务端关闭连接时,可能发生异常。我们做个试验看看。
首先写一个简单程序观察一下AWS S3服务端的Keepalive时间
String url = "一个可以访问的S3下载地址";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet(url);
httpClient.execute(request, response -> {
String content = EntityUtils.toString(response.getEntity());
System.out.println(content);
return content;
});
Thread.sleep(99999);
Wireshark抓包观察HTTP响应报文后,经过多久进入四次挥手:

可以看出服务端发送FIN包距离上一个请求的时间大概是23秒,也就是AWS S3服务端的Keepalive时间大致为23秒。
接着我们模拟客户端在服务端关闭连接的同时发送请求的场景,看看能否复现NoHttpResponseException错误:
String url = "http://s3-us-west-1.amazonaws.com/sdpcs-prod-awsca/88ea9001-bad0-4b46-86e5-e6bc518c9fdc?Expires=1718171230&response-content-type=image/jpeg&response-cache-control=max-age%3D157680000&AWSAccessKeyId=AKIAI7P7PYLVYWVVYTLQ&Signature=iCeE6%2FIHtxmOarOc3Q1hUowWqDc%3D";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet(url);
for (int i = 0; i < 100000; i++) {
httpClient.execute(request, response -> {
String content = EntityUtils.toString(response.getEntity());
System.out.println(content);
return content;
});
Thread.sleep(23000);
}
多执行几次,就能复现出NoHttpResponseException错误:
六月 14, 2019 2:09:14 下午 org.apache.http.impl.execchain.RetryExec execute
信息: I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {}->http://s3-us-west-1.amazonaws.com:80: The target server failed to respond
六月 14, 2019 2:09:14 下午 org.apache.http.impl.execchain.RetryExec execute
信息: Retrying request to {}->http://s3-us-west-1.amazonaws.com:80
分析抓包:

可以看到2400号请求距离上一个请求23秒,然后在服务端还未收到2400号请求时,客户端就收到了服务端发来的FIN请求,进入了四次挥手流程。然后当服务端收到2400号请求后,响应RST请求,导致客户端提示错误。
HttpClient提供了关闭空闲连接的功能:
CloseableHttpClient httpClient = HttpClients.custom()
.evictIdleConnections(5, TimeUnit.SECONDS)
.build();
我们设置一个低于S3 Keepalive的时间再次执行,就不会出现NoHttpResponseException错误了。
除了在客户端设置小于服务端的Keepalive时间,还有一种做法是在出现NoHttpResponseException时进行重试,也是可以的,还可以减少TIME_WAIT数量。
本文独立博客地址:记一次NoHttpResponseException问题排查 | 木杉的博客
记一次NoHttpResponseException问题排查的更多相关文章
- 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)
原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.
- 记一次用arthas排查jvm中CPU占用过高问题
记一次使用arthas排查jvm中CPU占用过高问题.这工具屌爆了 碾压我目前使用的全部JVM工具. 安装 小试 curl -O https://arthas.aliyun.com/arthas-bo ...
- 记一次tomcat故障排查(转)
1~1024之间的端口号是保留端口,通常是为特定目的预留的.虽然你的问题不是由于保留端口引起的,但是仍然建议你不要随意使用保留端口作为自定义服务的端口,如果你能早早遵循这一规则压根就不会遇到这个问题. ...
- 记一次jvm异常排查及优化
为方便自己查看,根据工作遇到的问题,转载并整理以下jvm优化内容 有次接到客服反馈,生产系统异常,无法访问.接到通知紧急上后台跟踪,查看了数据库死锁情况--正常,接着查看tomcat 内存溢出--正常 ...
- 【问题记录】记一次ConnectionTimeout问题排查
最近做性能测试时,发现连接第三方系统时会有约1%的交易提示如下错误 nested exception is org.apache.commons.httpclient.ConnectTimeoutEx ...
- 记一次Windb死锁排查
正在开会,突然线上站点线程数破千.然后一群人现场dump分析. 先看一眼线程运行状态 !eeversion 发现CPU占用并不高,19%,937条线程正在运行. 看看他们都在干什么. ~* e !cl ...
- 记一次OOM问题排查过程
上周运维反馈线上程序出现了OOM,程序日志中的输出为 Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMe ...
- 记一次使用windbg排查内存泄漏的过程
一.背景 近期有一个项目在运行当中出现一些问题,程序顺利启动,但是观察一阵子后发现内存使用总量在很缓慢地升高, 虽然偶尔还会往下降一些,但是总体还是不断上升:内存运行6个小时候从33M上升到80M: ...
- 记一次oom问题排查
大家好,我是大彬~ 今天给大家分享最近出现的OOM问题. 上周五早上,测试同学反馈测试环境的子系统服务一直超时,请求没有响应. 收到这个问题之后,我有点纳闷,最近这个系统也没有改动代码逻辑,怎么会突然 ...
随机推荐
- 获得APP的包名package和activity
方法一: Aapt dumpbadging xxxx.apk(包的路径) 第一个框为包名 第二个框为主Activity名 方法二: 如果你装了Appium 可以这么操作下 进入设置页,选择APK 路 ...
- 【C语言】创建一个函数,并调用比较两个数的大小
#include <stdio.h> int max(int x,int y) { if(x>=y) return x; else return y; } main() { int ...
- EntityFramework中使用sql语句
https://blog.csdn.net/yangyangafan/article/details/77602133 EntityFramework操作数据库谜一般的方便还不用写数据库,但前提是很简 ...
- python中getpass模块
1 import getpass 2 name = input('请输入你的名字:') 3 passwd = getpass.getpass('请输入你的密码:') 4 print(name) 5 p ...
- ASP.NET Core搭建多层网站架构【5-网站数据库实体设计及映射配置】
2020/01/29, ASP.NET Core 3.1, VS2019, EntityFrameworkCore 3.1.1, Microsoft.Extensions.Logging.Consol ...
- mysql取出字段数据的精度
$field = 'convert(avg(mood),decimal(4,0)) mood,convert(avg(hrv),decimal(4,0)) hrv,convert(avg(heart_ ...
- 20141110的alltosun面试
今天周一,是校招的第一天,心情有点紧张,不过可以和很多同学一起去,是我紧张的心情变得稍微安静些.面试进行的时候,是学长2哥面的我,总体感觉自己的表现很糟糕,在公共场合发表言论或者演讲,一直是我的一个弱 ...
- html和css的重难点知识
目录 html总难点总结: 1. 块级标签与内联标签的区别 1.1 块级标签: 1.2 内联标签: 2. 选择器 2.1 定义 2.2 选择器的分类 2.1 选择器的分类 3. css中margin, ...
- float,double,int的区别
int num = 10/4; float num2 = 10/4; double num3 = 10.0/4.0; System.out.println(num); System.out.print ...
- ES-elasticsearch安装-linux
(1)安装JDK(ES是使用java开发的) (2)安装ES(虚拟机内存大于一个g) 1)创建普通用户启动 2)非常占用内存(默认1个g的内存) (3)创建一个普通用户(用于启动ES) groupad ...