关于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是非常重要的,在涉及到一致性的请求中,如果一次请求失败之后重新尝试,那中间会发生什么问题是未知的(代码返回失败!=请求失败)

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