2

In my test application I execute consecutive HttpGet requests to the same host with Apache HttpClient but upon each next request it turns out that the previous HttpConnection is closed and the new HttpConnection is created.

I use the same instance of HttpClient and don't close responses. From each entity I get InputStream, read from it with Scanner and then close the Scanner. I have tested KeepAliveStrategy, it returns true. The time between requests doesn't exceed keepAlive or connectionTimeToLive durations.

Can anyone tell me what could be the reason for such behavior?

Updated

I have found the solution. In order to keep the HttpConnecton alive it is necessary to set HttpClientConnectionManager when building HttpClient. I have used 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
} 

Updated 2

In general checking connections with conn.isOpen() is not proper way to check the connections state because: "Internally HTTP connection managers work with instances of ManagedHttpClientConnection acting as a proxy for a real connection that manages connection state and controls execution of I/O operations. If a managed connection is released or get explicitly closed by its consumer the underlying connection gets detached from its proxy and is returned back to the manager. Even though the service consumer still holds a reference to the proxy instance, it is no longer able to execute any I/O operations or change the state of the real connection either intentionally or unintentionally." (HttpClent Tutorial)

As have pointed @oleg the proper way to trace connections is using the logger.

snptc
  • 33
  • 1
  • 6

3 Answers3

4

First of all you need to make sure remote server you're working with does support keep-alive connections. Just simply check whether remote server does return header Connection: Keep-Alive or Connection: Closed in each and every response. For Close case there is nothing you can do with that. You can use this online tool to perform such check.

Next, you need to implement the ConnectionKeepAliveStrategy as defined in paragraph #2.6 of this manual. Note that you can use existent DefaultConnectionKeepAliveStrategy since HttpClient version 4.0, so that your HttpClient will be constructed as following:

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

That will ensure you HttpClient instance will reuse the same connection via keep-alive mechanism if it is being supported by server.

Kostiantyn
  • 1,856
  • 11
  • 13
  • Thank you for answer. Server returns Connection: keep-alive and I use ConnectionKeepAliveStrategy. This is why I don't understand the cause of connetction closing... – snptc Sep 05 '17 at 12:37
  • Can you please post a method or piece of code which constructs http client and then sends consecutive gets? – Kostiantyn Sep 05 '17 at 12:39
  • I see custome implementation of the KeepAliveStrategy here https://github.com/otkp/29-rta-registration/blob/dbfaa6b7aa296505decfc3be63b71934a24d5c4d/epragati-rta-reg/epragati-reg-sp-scheduler/src/main/java/org/epragati/SPSchedulerApp.java#L97 – Xelian Aug 11 '22 at 15:40
0

Your application must be closing response objects in order to ensure proper resource de-allocation of the underlying connections. Upon response closure HttpClient keeps valid connections alive and returns them back to the connection manager (connection pool).

I suspect your code simply leaks connections and every request ens up with a newly created connection while all previous connections keep on piling up in memory.

ok2c
  • 26,450
  • 5
  • 63
  • 71
  • **oleg:** *"Your application must be closing response objects in order to ensure proper resource de-allocation of the underlying connections."* From the official [HttpClient Tutorial](https://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e145): *"The difference between closing the content stream and closing the response is that the former will attempt to keep the underlying connection alive by consuming the entity content while the latter immediately shuts down and discards the connection."* That's why I close the InputStream rather than the response itself. – snptc Sep 06 '17 at 08:04
  • I know what the tutorial says. I do not see your code closing InputStream anywhere. I do see Scanner getting closed but I am not sure that also results in closure of InputStream. Closing the response ensures that the connection goes back to the pool _no matter what_ and is the best practice. – ok2c Sep 06 '17 at 10:05
  • If you are reasonably sure your code does not leak connection, run it with the connection management / request execution logging turned on as described here http://hc.apache.org/httpcomponents-client-4.5.x/logging.html and see whether or not connections are kept alive upon release back to the pool – ok2c Sep 06 '17 at 10:07
  • **oleg:** *"I do see Scanner getting closed but I am not sure that also results in closure of InputStream."* From [Java API Specification](https://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html): *"When a Scanner is closed, it will close its input source if the source implements the Closeable interface."* I've tried to close responses but that doesn't help to keep connection alive. – snptc Sep 06 '17 at 14:35
  • Your code still ought to close CloseableHttpResponse in a try-finally regardless. Please produce the context / wire log of the session and post it here or at users@hc.apache.org. – ok2c Sep 06 '17 at 14:38
  • Thank you, Oleg. I have found the solution. I have set BasicHttpClientConnectionManager when building the HttpClient. Now all works fine and connection is kept alive whether I close the response or not. – snptc Sep 06 '17 at 16:21
  • Basic connection manager should still keep the underlying connection alive as long consecutive requests target the same host. – ok2c Sep 07 '17 at 08:06
  • It keeps but after some number of requests the server returns Connection: close. Does it depend on the number of requests or the amount of time? Now I'm trying to perform this task with PoolingHttpClientConnectionManager and with concurrent threads but the former issue arises: after first request the connection is closed. You are right I really need to test this machinery with logger. – snptc Sep 08 '17 at 14:20
0

From the example at 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. 

So as @oleg said you need to close the HttpResponse before checking the connection status.

Nicola Ambrosetti
  • 2,567
  • 3
  • 22
  • 38
  • I've tried. The result is the same... Moreover the HttpClient Tutorial states that closing the response will close the underlying connection (see my comment above). – snptc Sep 06 '17 at 09:04