上传文件程序会有一定的概率提示错误,错误率大概在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问题排查的更多相关文章

  1. 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)

    原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.

  2. 记一次用arthas排查jvm中CPU占用过高问题

    记一次使用arthas排查jvm中CPU占用过高问题.这工具屌爆了 碾压我目前使用的全部JVM工具. 安装 小试 curl -O https://arthas.aliyun.com/arthas-bo ...

  3. 记一次tomcat故障排查(转)

    1~1024之间的端口号是保留端口,通常是为特定目的预留的.虽然你的问题不是由于保留端口引起的,但是仍然建议你不要随意使用保留端口作为自定义服务的端口,如果你能早早遵循这一规则压根就不会遇到这个问题. ...

  4. 记一次jvm异常排查及优化

    为方便自己查看,根据工作遇到的问题,转载并整理以下jvm优化内容 有次接到客服反馈,生产系统异常,无法访问.接到通知紧急上后台跟踪,查看了数据库死锁情况--正常,接着查看tomcat 内存溢出--正常 ...

  5. 【问题记录】记一次ConnectionTimeout问题排查

    最近做性能测试时,发现连接第三方系统时会有约1%的交易提示如下错误 nested exception is org.apache.commons.httpclient.ConnectTimeoutEx ...

  6. 记一次Windb死锁排查

    正在开会,突然线上站点线程数破千.然后一群人现场dump分析. 先看一眼线程运行状态 !eeversion 发现CPU占用并不高,19%,937条线程正在运行. 看看他们都在干什么. ~* e !cl ...

  7. 记一次OOM问题排查过程

    上周运维反馈线上程序出现了OOM,程序日志中的输出为 Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMe ...

  8. 记一次使用windbg排查内存泄漏的过程

    一.背景 近期有一个项目在运行当中出现一些问题,程序顺利启动,但是观察一阵子后发现内存使用总量在很缓慢地升高, 虽然偶尔还会往下降一些,但是总体还是不断上升:内存运行6个小时候从33M上升到80M: ...

  9. 记一次oom问题排查

    大家好,我是大彬~ 今天给大家分享最近出现的OOM问题. 上周五早上,测试同学反馈测试环境的子系统服务一直超时,请求没有响应. 收到这个问题之后,我有点纳闷,最近这个系统也没有改动代码逻辑,怎么会突然 ...

随机推荐

  1. Go 后端主要做什么

    漫谈 Go 语言后端开发 :https://blog.csdn.net/u010986776/article/details/87276303 Golang 资深后端工程师要了解的知识:https:/ ...

  2. laravel框架学习笔记

    一.laravel的安装 1.composer 作用:主要管理php中的依赖关系(类似于yum源) 可以安装的软件: curl    //主要用到微信开发中 upload //文本操作 excel / ...

  3. python-turtle-画雪花-2种方法及效果的详解

    1.方法一: 代码: #python3.8 #xuguojun #2020.1.30 #导出模块 import turtle as t import random as r #定义画雪 def dra ...

  4. 搭建robotframework环境

    1.安装rf 执行命令:pip install robotframework; 2.安装seleniumlibrary库 执行命令:pip install --upgrade robotframewo ...

  5. Android9 http网络请求的问题

    Google表示,为保证用户数据和设备的安全,针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的 ...

  6. Python日期

    1. datatime from datetime import datetime, date now = datetime.now() print(now) # 2020-01-20 01:24:0 ...

  7. 安卓之文本视图TextView及跑马灯效果

    一.基本属性和设置方法 二.跑马灯用到的属性与方法说明 三.省略方式的取值说明 四.跑马灯效果案例代码   (1)布局xml文件 <?xml version="1.0" en ...

  8. pl/sql修改data

    1,对于语句要包含rowid!

  9. 三 模拟实现顺序表ArrayList

    /** * 顺序表,重点是数组动态扩容,插入 * 底层采用数组,长度可以动态变化,此处采用增长一倍 *  java.util.ArrayList每次增长50% *  int newCapacity = ...

  10. pdf.js的使用(2)新的需求已经出现,怎么能够停止不前(迪迦奥特曼主题曲)哈哈哈。^_^

    来,咱们看图说事 按钮1,2是pdf.js自带的,分别对应顺时针旋转90度,逆时针旋转90度.于是乎又要我做一个旋转180度的按钮,诺!按钮3来了. 1.别怂,干!首先顺藤摸瓜,看按钮1,2的html ...