關於httpclient 連接失效引發的問題

 一 排查過程

使用的httpclient客戶端,版本是:

<dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.3.2</version>
</dependency>

在生產環境發現有時會報錯:

org.apache.http.NoHttpResponseException: The target server failed to respond

懷疑是Nginx超時之後連接不可用,但是連接池裏面沒有剔除導致,爲了簡化排錯,在本地直接設置了一個線程循環跑,把sleep時間調整成Nginx超時時間5s,代碼如下:

DefaultHttpClient httpClient = new DefaultHttpClient();
        httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
        HttpGet httpGet = new HttpGet("http://192.168.31.176/index_10s.html");
        httpGet.addHeader("User-Agent", USER_AGENT);
        CloseableHttpResponse httpResponse = null;
        for (int i = 0; i < 100; i++)
            try {
                httpResponse = httpClient.execute(httpGet);
                System.out.println("GET Response Status:: "
                        + httpResponse.getStatusLine().getStatusCode());
                BufferedReader reader = null;
                reader = new BufferedReader(new InputStreamReader(
                        httpResponse.getEntity().getContent()));
                String inputLine;
                StringBuffer response = new StringBuffer();
                while ((inputLine = reader.readLine()) != null) {
                    response.append(inputLine);
                }
                Thread.sleep(5000);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

這裏把httpclient設置成了不重試的機制。在跑了之後,發現確實出現了NoHttpResponseException報錯。
把當時的包抓下來,看到如下所示:
image

  1. 首先是三次建聯過程;
  2. 中間兩個get都成功,注意相差了5s,這是Nginx的keepalive時間;
  3. 下面是關鍵過程:
    image
    我們看到,
    (1)在首個包發送完成之後5s,已經到達Nginx的keepalive時間,此時服務端主動斷開連接,發送了FIN包(注意這裏的客戶端服務端是指的現在192是客戶端(發起http請求的一方),服務端指的是176(Nginx))。
    (2)客戶端響應服務端請求,發送ACK包,此時192處於CLOSE_WAIT狀態,176處於FIN_WAIT2狀態,此時192仍然可以向176發送數據,但是176不會再向192發送數據,但是仍人可以接收192的數據;
    (3)這時候我們發現,192向176發送了get請求,這個請求自然就被rst了;

 二 httpclient使用

通常我們在使用的時候,不會考慮是否需要設置retry,實際上設置retry爲false是非常重要的,在涉及到一致性的請求中,如果一次請求失敗之後重新嘗試,那中間會發生什麼問題是未知的(代碼返回失敗!=請求失敗)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章