74

我对一堆 URL 进行了循环,对于每个 URL,我都在执行以下操作:

private String doQuery(String url) {

  HttpGet httpGet = new HttpGet(url);
  setDefaultHeaders(httpGet); // static method
  HttpResponse response = httpClient.execute(httpGet);   // httpClient instantiated in constructor

  int rc = response.getStatusLine().getStatusCode();

  if (rc != 200) {
    // some stuff...
    return;
  }

  HttpEntity entity = response.getEntity();

  if (entity == null) {
    // some stuff...
    return;
  }

  // process the entity, get input stream etc

}

第一个查询很好,第二个抛出这个异常:

线程“主”java.lang.IllegalStateException 中的异常:SingleClientConnManager 的使用无效:连接仍然分配。确保在分配另一个连接之前释放连接。在 org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:199) 在 org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:173)......

这只是一个简单的单线程应用程序。我怎样才能释放这个连接?

4

12 回答 12

98

Httpcomponents 4.1 推荐的方法是关闭连接并释放任何底层资源:

EntityUtils.consume(HttpEntity)

其中HttpEntity传递的是响应实体。

于 2011-08-22T19:22:35.803 回答
34

这似乎很好用:

      if( response.getEntity() != null ) {
         response.getEntity().consumeContent();
      }//if

即使您没有打开其内容,也不要忘记使用该实体。例如,您期望响应中的 HTTP_OK 状态并且没有得到它,您仍然必须使用实体!

于 2012-02-29T11:57:25.433 回答
23

要回答我自己的问题:要释放连接(以及与请求关联的任何其他资源),您必须关闭 HttpEntity 返回的 InputStream:

InputStream is = entity.getContent();

.... process the input stream ....

is.close();       // releases all resources

文档

于 2011-01-23T19:06:38.623 回答
19

从 4.2 版本开始,他们引入了一种更方便的方法来简化连接释放:HttpRequestBase.releaseConnection()

于 2012-09-12T13:11:17.477 回答
13

我正在附上一个专门针对 Apache HttpClient 4.0.1 的详细答案。我正在使用这个 HttpClient 版本,因为它是由 WAS v8.0 提供的,我需要在同样由 WAS v8.0 提供的 Apache Wink v1.1.1 中使用提供的 HttpClient 来对 Sharepoint 进行一些经过 NTLM 身份验证的 REST 调用.

引用 Apache HttpClient 邮件列表中的 Oleg Kalnichevski 的话:

几乎所有这些代码都是不必要的。(1) 只要实体内容被消费到流的末尾,HttpClient就会自动释放底层连接;(2) HttpClient 会在读取响应内容时抛出任何 I/O 异常自动释放底层连接。在这种情况下不需要特殊处理。

事实上,这完全足以确保正确释放资源:

HttpResponse rsp = httpclient.execute(target, req); 
HttpEntity entity = rsp.getEntity(); 
if (entity != null) {
     InputStream instream = entity.getContent();
     try {
         // process content
     } finally {
         instream.close();
         // entity.consumeContent() would also do
     } 
}

这就对了。

资源

于 2015-04-10T21:28:29.890 回答
7

如果不消耗响应,则可以使用以下代码中止请求:

// Low level resources should be released before initiating a new request
HttpEntity entity = response.getEntity();

if (entity != null) {
    // Do not need the rest
    httpPost.abort();
}

参考:http ://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e143

Apache HttpClient 版本:4.1.3

于 2012-05-07T12:23:11.470 回答
4

当我在多线程环境(Servlet)中使用 HttpClient 时,我遇到了这个问题。一个 servlet 仍然保持连接,而另一个想要获得连接。

解决方案:

4.0版使用ThreadSafeClientConnManager

4.2版使用PoolingClientConnectionManager

并设置这两个设置器:

setDefaultMaxPerRoute
setMaxTotal
于 2012-12-07T10:51:09.433 回答
3

我正在使用 HttpClient 4.5.3,CloseableHttpResponse#close对我有用。

    CloseableHttpResponse response = client.execute(request);

    try {
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity);
        checkResult(body);
        EntityUtils.consume(entity);
    } finally {
        response.close();
    }

使用 Java7 及更高版本:

try (CloseableHttpResponse response = client.execute(request)) {
   ...
}
于 2017-04-03T07:23:37.990 回答
2

HTTP HEAD 请求的处理方式必须略有不同,因为 response.getEntity() 为空。相反,您必须捕获传递给 HttpClient.execute() 的 HttpContext 并检索连接参数以关闭它(无论如何在 HttpComponents 4.1.X 中)。

HttpRequest httpRqst = new HttpHead( uri );
HttpContext httpContext = httpFactory.createContext();
HttpResponse httpResp = httpClient.execute( httpRqst, httpContext );

...

// Close when finished
HttpEntity entity = httpResp.getEntity();
if( null != entity )
  // Handles standard 'GET' case
  EntityUtils.consume( entity );
else {
  ConnectionReleaseTrigger  conn =
      (ConnectionReleaseTrigger) httpContext.getAttribute( ExecutionContext.HTTP_CONNECTION );
  // Handles 'HEAD' where entity is not returned
  if( null != conn )
    conn.releaseConnection();
}

HttpComponents 4.2.X 向 HttpRequestBase 添加了 releaseConnection() 来简化此操作。

于 2014-11-04T21:48:31.823 回答
1

如果要重新使用连接,则必须在每次使用后完全使用内容流,如下所示:

EntityUtils.consume(response.getEntity())

注意:即使状态码不是 200,您也需要使用内容流。不这样做会在下次使用时引发以下问题:

线程“主”java.lang.IllegalStateException 中的异常:SingleClientConnManager 的使用无效:连接仍然分配。确保在分配另一个连接之前释放连接。

如果它是一次性使用,那么简单地关闭连接将释放与其关联的所有资源。

于 2017-08-31T12:48:14.113 回答
0

强烈建议使用处理程序来处理响应。

client.execute(yourRequest,defaultHanler);

它将通过consume(HTTPENTITY)方法自动释放连接。

处理程序示例:

private ResponseHandler<String> defaultHandler = new ResponseHandler<String>() {
    @Override
    public String handleResponse(HttpResponse response)
        throws IOException {
        int status = response.getStatusLine().getStatusCode();

        if (status >= 200 && status < 300) {
            HttpEntity entity = response.getEntity();
            return entity != null ? EntityUtils.toString(entity) : null;
        } else {
            throw new ClientProtocolException("Unexpected response status: " + status);
        }
    }
};
于 2018-01-30T07:32:45.190 回答
-1

我遇到了同样的问题,并通过在方法结束时关闭响应来解决它:

try {
    // make the request and get the entity 
} catch(final Exception e) {
    // handle the exception
} finally {
    if(response != null) {
        response.close();
    }
}
于 2015-05-05T00:54:25.340 回答