如何使用Apache HttpClient获取持久性HttpConnection?

时间:2017-09-05 10:45:44

标签: java apache-httpclient-4.x

在我的测试应用程序中,我使用 Apache HttpClient 对同一主机执行连续的HttpGet请求,但是在每次下一个请求时都会发现前一个HttpConnection已关闭且新的HttpConnection已创建。

我使用相同的HttpClient实例并且不要关闭响应。我从每个实体获取InputStream,使用Scanner从中读取,然后关闭Scanner。我测试了KeepAliveStrategy,它返回true。请求之间的时间不超过keepAlive或connectionTimeToLive持续时间。

谁能告诉我这种行为可能是什么原因?

更新

我找到了解决方案。为了使HttpConnecton保持活动状态,在构建HttpClient时需要设置 HttpClientConnectionManager 。我使用了 BasicHttpClientConnectionManager

ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy() {
   @Override
   public long getKeepAliveDuration(HttpResponse response, HttpContext context)
   {
      long keepAlive = super.getKeepAliveDuration(response, context);
      if (keepAlive == -1)
         keepAlive = 120000;
      return keepAlive;
   }
};
HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager();
try (CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager) // without this setting connection is not kept alive 
            .setDefaultCookieStore(store)
            .setKeepAliveStrategy(keepAliveStrat)
            .setConnectionTimeToLive(120, TimeUnit.SECONDS)
            .setUserAgent(USER_AGENT)
            .build())
{   
   HttpClientContext context = new HttpClientContext();
   RequestConfig config = RequestConfig.custom()
           .setCookieSpec(CookieSpecs.DEFAULT)
           .setSocketTimeout(10000)
           .setConnectTimeout(10000)
           .build();
   context.setRequestConfig(config);
   HttpGet httpGet = new HttpGet(uri);
   CloseableHttpResponse response = httpClient.execute(httpGet, context);
   HttpConnection conn = context.getConnection();
   HttpEntity entity = response.getEntity();
   try (Scanner in = new Scanner(entity.getContent(), ENC))
   {
      // do something
   }
   System.out.println("open=" + conn.isOpen()); // now open=true 

   HttpGet httpGet2 = new HttpGet(uri2); // on the same host with other path

   // and so on
} 

更新2

通常,检查与conn.isOpen()的连接不是检查连接状态的正确方法,因为:"内部HTTP连接管理器使用ManagedHttpClientConnection实例作为管理实际连接的代理连接状态并控制I / O操作的执行。如果托管连接被释放或由其使用者显式关闭,则底层连接将从其代理中分离并返回给管理器。 即使服务使用者仍然拥有对代理实例的引用,它也无法有意或无意地执行任何I / O操作或更改实际连接的状态。" HttpClent Tutorial

正如@oleg所指出的,跟踪连接的正确方法是使用logger

3 个答案:

答案 0 :(得分:3)

首先,您需要确保您正在使用的远程服务器支持保持活动连接。只需检查远程服务器是否在每个响应中都返回标头Connection: Keep-AliveConnection: Closed。对于Close案例there is nothing,你可以做到这一点。您可以使用this online tool执行此类检查。

接下来,您需要实现this manual第2.6段中定义的ConnectionKeepAliveStrategy。请注意,您可以使用现有的DefaultConnectionKeepAliveStrategy since HttpClient version 4.0,以便将HttpClient构建如下:

HttpClient client = HttpClients.custom()
    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
    .build();

这将确保您HttpClient实例将通过保持活动机制重用相同的连接(如果服务器支持它)。

答案 1 :(得分:0)

您的应用程序必须关闭响应对象,以确保正确的资源取消分配底层连接。在响应关闭时,HttpClient保持有效连接处于活动状态并将它们返回给连接管理器(连接池)。

我怀疑你的代码只是泄漏了连接,并且每个请求都依赖于新创建的连接,而所有先前的连接都在堆积在内存中。

答案 2 :(得分:0)

来自HttpClient website的示例:

// In order to ensure correct deallocation of system resources
// the user MUST call CloseableHttpResponse#close() from a finally clause.
// Please note that if response content is not fully consumed the underlying
// connection cannot be safely re-used and will be shut down and discarded
// by the connection manager. 

因为@oleg说你需要在检查连接状态之前关闭HttpResponse。