HttpClient release connection 该放手的时候必须放手

来源:互联网 发布:博客推荐知乎 编辑:程序博客网 时间:2024/04/29 02:12

Apache commons 系列的HttpClient 相信大家都用过,选择它而非JDK 的java.net.HttpURLConnection ,是为了使用HttpClient 封装的几个实用的功能。

目前使用最多的版本还是httpclient-3.x ,在官网http://hc.apache.org/httpclient-3.x/tutorial.html 有这么一段示例代码:

import org.apache.commons.httpclient.*;import org.apache.commons.httpclient.methods.*;import org.apache.commons.httpclient.params.HttpMethodParams;import java.io.*;public class HttpClientTutorial {    private static String url = "http://www.apache.org/";  public static void main(String[] args) {    // Create an instance of HttpClient.    HttpClient client = new HttpClient();    // Create a method instance.    GetMethod method = new GetMethod(url);        // Provide custom retry handler is necessary    method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,     new DefaultHttpMethodRetryHandler(3, false));    try {      // Execute the method.      int statusCode = client.executeMethod(method);      if (statusCode != HttpStatus.SC_OK) {        System.err.println("Method failed: " + method.getStatusLine());      }      // Read the response body.      byte[] responseBody = method.getResponseBody();      // Deal with the response.      // Use caution: ensure correct character encoding and is not binary data      System.out.println(new String(responseBody));    } catch (HttpException e) {      System.err.println("Fatal protocol violation: " + e.getMessage());      e.printStackTrace();    } catch (IOException e) {      System.err.println("Fatal transport error: " + e.getMessage());      e.printStackTrace();    } finally {      // Release the connection.      method.releaseConnection();    }    }}

大部分人也是以这个为规范来使用的,但是注意有一段关于“Release the Connection”的说明:

This is a crucial step to keep things flowing. We must tell HttpClient that we are done with the connection and that it can now be reused. Without doing this HttpClient will wait indefinitely for a connection to free up so that it can be reused.
我看得也不是很明白,意思是我们必须在使用后调用

method.releaseConnection();
来告诉HttpClient 这个连接可以重用了。

这个在串行的处理中或许很有用,但是我所遇到的情况是多线程并发下,不能共享同一个HttpClient 实例,按照官方示例写好代码后,程序跑起来似乎没什么问题,但是随着时间的累计,有一天突然发现这个模块不工作了,查看了一下当前的网络连接,这个java 程序同一个地址保持着200多个CLOSE_WAIT 的连接,好吧,连接没有释放。

为什么没有释放?查看doc,有这样的说明:

Releases the connection being used by this HTTP method. In particular the connection is used to read the response(if there is one) and will be held until the response has been read. If the connection can be reused by other HTTP methods it is NOT closed at this point.

注意最后一句,如果该连接可以重用则不关闭,是“可以重用”,当然可以重用了,就在那儿等着我去重用,可是我都是新建的实例,怎么重用

查看源码,找到HttpClient 的构造方法,有一个可以指定HttpConnectionManager ,然后这个HttpConnectionManager 又有一个实现的构造:

public SimpleHttpConnectionManager(boolean alwaysClose)The connection manager created with this constructor will try to keep the connection open (alive) between consecutive requests if the alwaysClose parameter is set to false. Otherwise the connection manager will always close connections upon release.Parameters:alwaysClose - if set true, the connection manager will always close connections upon release.

显然alawaysClose 的默认值是false ,在释放后连接并不总是会关闭。

所以,必须

HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true));

当然,还有其它的解决方案,找到了一篇文章总结的比较全面:HttpClient容易忽视的细节——连接关闭

2 0
原创粉丝点击