NoHttpResponseException問題排查解決記錄分析
錯誤提示
上傳文件程序會有一定的概率提示錯誤,錯誤率大概在1%以下,錯誤信息是:
org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws.com:80 failed to respond
看著是上傳到S3的過程中發(fā)送了網(wǎng)絡錯誤?

通過查閱資料,發(fā)現(xiàn)了一篇比較好的文章:一次NoHttpResponseException問題分析解決。這個文章的觀點是會發(fā)生這個錯誤的原因是服務端關閉了連接,而客戶端還在使用該連接,導致服務端響應RST報文,客戶端收到RST報NoHttpResponseException異常。
Keepalive機制
為了說明這個場景,就要提一下Keepalive機制。Keepalive是HTTP的連接復用機制,在HTTP1.0時代,每個請求經(jīng)過三次握手后,只會傳輸一次HTTP請求和響應報文后,就進入四次揮手關閉連接了。而TCP建立連接和關閉連接的代價是比較大的,導致HTTP1.0的通道利用率較低,時延較高。針對這個問題,退出了Keepalive機制,一個TCP連接建立后,可以在上面發(fā)送多個HTTP報文,只有這個TCP連接的空閑時間達到超時時間,才會被關閉。HTTP1.1默認開啟Keepalive。這里的關閉行為可能發(fā)生在客戶端和服務端,比如客戶端的Keepalive超時時間更短,則客戶端就會先關閉連接,如果服務端配置的Keepalive超時時間更短,則服務端就會先關閉連接。
乍看起來無論那一邊關閉連接都沒什么問題,但是還是有細節(jié)需要注意。比如服務端關閉連接,發(fā)送FIN包,在這個FIN包發(fā)送但是還未到達客戶端期間,客戶端如果繼續(xù)復用這個TCP連接,發(fā)送HTTP請求報文的話,服務端會因為在四次揮手期間不接收報文而發(fā)送RST報文給客戶端,客戶端收到RST報文就會提示異常。
根據(jù)上面的理論知識,可以推測org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws.com:80 failed to respond這個錯誤發(fā)生的原因是因為我的程序的HttpClient的Keepalive時間大于S3服務器的,導致S3服務端關閉連接時,可能發(fā)生異常。我們做個試驗看看。
觀察AWS S3服務端的Keepalive時間
首先寫一個簡單程序觀察一下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響應報文后,經(jīng)過多久進入四次揮手:

可以看出服務端發(fā)送FIN包距離上一個請求的時間大概是23秒,也就是AWS S3服務端的Keepalive時間大致為23秒。
接著我們模擬客戶端在服務端關閉連接的同時發(fā)送請求的場景,看看能否復現(xiàn)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);
}多執(zhí)行幾次,就能復現(xiàn)出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號請求時,客戶端就收到了服務端發(fā)來的FIN請求,進入了四次揮手流程。然后當服務端收到2400號請求后,響應RST請求,導致客戶端提示錯誤。
HttpClient提供了關閉空閑連接的功能
CloseableHttpClient httpClient = HttpClients.custom()
.evictIdleConnections(5, TimeUnit.SECONDS)
.build();我們設置一個低于S3 Keepalive的時間再次執(zhí)行,就不會出現(xiàn)NoHttpResponseException錯誤了。
除了在客戶端設置小于服務端的Keepalive時間,還有一種做法是在出現(xiàn)NoHttpResponseException時進行重試,也是可以的,還可以減少TIME_WAIT數(shù)量。
以上就是NoHttpResponseException問題排查解決記錄分析的詳細內(nèi)容,更多關于NoHttpResponseException問題排查的資料請關注腳本之家其它相關文章!
相關文章
Spring?Boot?Reactor?整合?Resilience4j詳析
這篇文章主要介紹了Spring?Boot?Reactor整合Resilience4j詳析,文章通過引入pom包展開詳細介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-09-09
Linux系統(tǒng)下搭建Java開發(fā)環(huán)境
本文主要是記錄了如何在Linux環(huán)境下一步步安裝JAVA JDK環(huán)境,非常簡單實用,有需要的朋友可以參考下2014-10-10
SSM使用mybatis分頁插件pagehepler實現(xiàn)分頁示例
本篇文章主要介紹了SSM使用mybatis分頁插件pagehepler實現(xiàn)分頁示例,使用分頁插件的原因,簡化了sql代碼的寫法,實現(xiàn)較好的物理分頁,非常具有實用價值,需要的朋友可以參考下2018-03-03
SpringBoot整合SpringSecurity實現(xiàn)權(quán)限控制之實現(xiàn)多標簽頁
這篇文章主要介紹了SpringBoot整合SpringSecurity實現(xiàn)權(quán)限控制之實現(xiàn)多標簽頁,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11

