HttpClient 4.0.1 - 如何释放连接? [复制]

Posted

技术标签:

【中文标题】HttpClient 4.0.1 - 如何释放连接? [复制]【英文标题】:HttpClient 4.0.1 - how to release connection? [duplicate] 【发布时间】:2011-01-23 18:35:58 【问题描述】:

我对一堆 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


第一个查询没问题,第二个抛出这个异常:

线程“main”中的异常 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)......

这只是一个简单的单线程应用程序。如何释放此连接?

【问题讨论】:

相关:Exception using HttpRequest.execute(): Invalid use of SingleClientConnManager: connection still allocated 【参考方案1】:

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

EntityUtils.consume(HttpEntity)

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

【讨论】:

这是 4.1 的正确答案。 Richard H 的那个对我不起作用。 我也面临同样的问题。此解决方案有效,但并未涵盖所有可能的情况。我认为 httpClient 正在被重用。如果某些请求失败,正如您对 HTTP 所期望的那样,不清楚如何释放分配的资源。即使按照 Apache 提供的多个不同异常处理程序的示例,我仍然面临同样的问题:( @Rafael 如果在获取响应实体句柄之前抛出异常,您可以使用 HttpRequestBase.releaseConnection() 或 HttpUriRequest.abort()。 EntityUtils类中没有consume方法developer.android.com/reference/org/apache/http/util/… 如果出现异常并且在这种情况下可关闭的响应对象将为空怎么办?如果我不使用资源尝试,我们如何释放连接?因为我现在不能调用 response.close() 。谢谢【参考方案2】:

这似乎很好用:

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

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

【讨论】:

consumeContent() 现在 (4.1.2) 已弃用。一个单行代替:EntityUtils.consume(response.getEntity()); @Snicolas 如果我们不 consumeContent() 实体会发生什么 不确定,但我会说你泄漏了内存。我记得你也收到了一条错误消息。 对于那些想知道的人的后续行动:如果您不使用实体,HttpClient 将认为该请求仍在进行中。一旦这些未使用的请求堆积了足够多,后续请求将无限期阻塞。 现在似乎已被弃用【参考方案3】:

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

InputStream is = entity.getContent();

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

is.close();       // releases all resources

来自docs

【讨论】:

我不这么认为,我这样做了,我得到“SingleClientConnManager 的无效使用:连接仍然分配”。它可能通过我可以从 httpClient 获得的 ClientConnectionManager 来完成,但它似乎不对 我也没有。你在重用 HTTPClient 吗?你如何确保它在请求失败时释放资源? 我可以确认通过关闭 InputStream 我看到了调试消息:org.apache.http.impl.conn.BasicClientConnectionManager(18314): Releasing connection 我确认这工作正常。谢谢。 (顺便说一句。使用 HttpComponents-4.1 "EntityUtils.consume(httpEntity)" 也可以完成这项工作)。 Richard 是对的,在收到的输入流上调用“close()”通知观察者关闭,然后观察者关闭流本身并将连接标记为“可重用”(httpclient 版本 4.5.1)【参考方案4】:

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

【讨论】:

【参考方案5】:

我正在附上一个专门针对 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
      

就是这样。

Source

【讨论】:

...如果实体为空会发生什么,然后会发生什么?我们不需要关闭连接吗? @cecemel - 嗯,是的......但是你想处理它。这取决于情况的范围。 EntityUtils.consume(entity) 足够了吗?如果是的话,和关闭instream一样吗? @asgs - 我会坚持使用 finally 块中的两行代码。 @asgs - 这取决于 Javadoc 是否适用于 v4.0.1。我认为 EntityUtils 在 v4.0.1 之后的后续版本中成为首选方式。我的回答中的邮件列表摘录是从 v4.0.1 开始的。【参考方案6】:

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

// 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

【讨论】:

这是一个很好的回应。【参考方案7】:

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

解决方案:

4.0 版使用ThreadSafeClientConnManager

4.2 版使用PoolingClientConnectionManager

并设置这两个设置器:

setDefaultMaxPerRoute
setMaxTotal

【讨论】:

【参考方案8】:

我正在使用 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)) 
   ...

【讨论】:

从 Java 7 开始使用:try (CloseableHttpResponse response = client.execute(request)) ... "使用 CloseableHttpClient#close 为我工作。"在该代码中,您关闭的是响应而不是客户端 - 差别很大。 @JL_SO 关闭响应会将连接从客户端释放回连接池管理器。客户端可以在以后租用另一个连接。如果您关闭了客户端,则需要创建一个新的客户端实例,这在大多数情况下是不必要的。 @FanJin 是的,这是真的,但我的意思是您说“Client#close 为我工作”,然后粘贴了执行 Response#close 的代码(而不是 client#close)。跨度> 你是对的,我已经更新了我的答案。【参考方案9】:

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() 来简化此操作。

【讨论】:

【参考方案10】:

如果你想重新使用连接,那么你必须在每次使用后完全消费内容流,如下所示:

EntityUtils.consume(response.getEntity())

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

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

如果是一次性使用,那么只需关闭连接就会释放与其关联的所有资源。

【讨论】:

【参考方案11】:

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

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);
        
    
;

【讨论】:

【参考方案12】:

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

try 
    // make the request and get the entity 
 catch(final Exception e) 
    // handle the exception
 finally 
    if(response != null) 
        response.close();
    

【讨论】:

以上是关于HttpClient 4.0.1 - 如何释放连接? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient 释放连接

释放 HttpClient 4.1.x 的连接以使用一个 HttpClient 实例顺序执行

Zuul1压测无法释放底层httpclient的连接池

HttpClient在响应流完全读取之前被释放,导致连接关闭异常

org.apache.httpcomponents:httpclient 工具类

HttpClient连接池耗尽引发雪崩问题