JAVA-15023: Chnages made for upgrading Apache HttpClient Connection M… (#14402)

This commit is contained in:
Bipin kumar 2023-08-22 21:56:25 +05:30 committed by GitHub
parent 11c984b995
commit 4aba635055
7 changed files with 667 additions and 203 deletions

View File

@ -1,69 +1,71 @@
package com.baeldung.httpclient.conn; package com.baeldung.httpclient.conn;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.http.HeaderElement; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.http.HeaderElementIterator; import org.apache.hc.client5.http.HttpRoute;
import org.apache.http.HttpClientConnection; import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.http.HttpException; import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.http.HttpHost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.http.HttpResponse; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.http.client.ClientProtocolException; import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.http.client.config.RequestConfig; import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.http.client.methods.HttpGet; import org.apache.hc.client5.http.io.ConnectionEndpoint;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.hc.client5.http.io.LeaseRequest;
import org.apache.http.config.SocketConfig; import org.apache.hc.core5.http.HeaderElement;
import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.hc.core5.http.HeaderElements;
import org.apache.http.conn.ConnectionPoolTimeoutException; import org.apache.hc.core5.http.HttpHost;
import org.apache.http.conn.ConnectionRequest; import org.apache.hc.core5.http.HttpResponse;
import org.apache.http.conn.routing.HttpRoute; import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.http.impl.client.HttpClients; import org.apache.hc.core5.http.message.StatusLine;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager; import org.apache.hc.core5.http.protocol.BasicHttpContext;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.http.message.BasicHeaderElementIterator; import org.apache.hc.core5.pool.PoolStats;
import org.apache.http.protocol.HTTP; import org.apache.hc.core5.util.Args;
import org.apache.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue;
import org.apache.http.protocol.HttpCoreContext; import org.apache.hc.core5.util.Timeout;
import org.apache.http.protocol.HttpRequestExecutor; import org.junit.Assert;
import org.apache.http.util.EntityUtils;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
public class HttpClientConnectionManagementLiveTest { public class HttpClientConnectionManagementLiveTest {
// Example 2.1. Getting a Connection Request for a Low Level Connection (HttpClientConnection) // Example 2.1. Getting a Connection Request for a Low Level Connection (HttpClientConnection)
@Test @Test
public final void whenLowLevelConnectionIsEstablished_thenNoExceptions() throws ConnectionPoolTimeoutException, InterruptedException, ExecutionException { public final void whenLowLevelConnectionIsEstablished_thenNoExceptions() throws ExecutionException, InterruptedException, TimeoutException {
try (BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager()) { BasicHttpClientConnectionManager connMgr = new BasicHttpClientConnectionManager();
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 80)); HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 443));
final ConnectionRequest connRequest = connManager.requestConnection(route, null); final LeaseRequest connRequest = connMgr.lease("some-id", route, null);
assertNotNull(connRequest.get(1000, TimeUnit.SECONDS)); assertNotNull(connRequest.get(Timeout.ZERO_MILLISECONDS));
} connMgr.close();
} }
// Example 3.1. Setting the PoolingHttpClientConnectionManager on a HttpClient // Example 3.1. Setting the PoolingHttpClientConnectionManager on a HttpClient
@Test @Test
public final void whenPollingConnectionManagerIsConfiguredOnHttpClient_thenNoExceptions() throws ClientProtocolException, IOException { public final void whenPollingConnectionManagerIsConfiguredOnHttpClient_thenNoExceptions() throws IOException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom() CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager) .setConnectionManager(poolingConnManager)
.build(); .build();
client.execute(new HttpGet("https://www.baeldung.com")); client.execute(new HttpGet("https://www.baeldung.com"));
assertTrue(poolingConnManager.getTotalStats() assertTrue(poolingConnManager.getTotalStats()
.getLeased() == 1); .getLeased() == 1);
client.close();
poolingConnManager.close();
} }
// Example 3.2. Using Two HttpClients to Connect to One Target Host Each // Example 3.2. Using Two HttpClients to Connect to One Target Host Each
@Test @Test
public final void whenTwoConnectionsForTwoRequests_thenNoExceptions() throws InterruptedException { public final void whenTwoConnectionsForTwoRequests_thenNoExceptions() throws InterruptedException, IOException {
HttpGet get1 = new HttpGet("https://www.baeldung.com"); HttpGet get1 = new HttpGet("https://www.baeldung.com");
HttpGet get2 = new HttpGet("https://www.google.com"); HttpGet get2 = new HttpGet("https://www.google.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
@ -81,38 +83,52 @@ public class HttpClientConnectionManagementLiveTest {
thread1.join(); thread1.join();
thread2.join(); thread2.join();
assertTrue(connManager.getTotalStats() Assert.assertTrue(connManager.getTotalStats()
.getLeased() == 0); .getLeased() == 0);
client1.close();
client2.close();
connManager.close();
} }
// Example 4.1. Increasing the Number of Connections that Can be Open and Managed Beyond the default Limits // Example 4.1. Increasing the Number of Connections that Can be Open and Managed Beyond the default Limits
@Test @Test
public final void whenIncreasingConnectionPool_thenNoEceptions() { public final void whenIncreasingConnectionPool_thenNoExceptions() {
try (PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager()) { PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(5); connManager.setMaxTotal(5);
connManager.setDefaultMaxPerRoute(4); connManager.setDefaultMaxPerRoute(4);
HttpHost host = new HttpHost("www.baeldung.com", 80); HttpHost host = new HttpHost("www.baeldung.com", 80);
connManager.setMaxPerRoute(new HttpRoute(host), 5); connManager.setMaxPerRoute(new HttpRoute(host), 5);
} connManager.close();
} }
// Example 4.2. Using Threads to Execute Connections // Example 4.2. Using Threads to Execute Connections
@Test @Test
public final void whenExecutingSameRequestsInDifferentThreads_thenExecuteReuqest() throws InterruptedException { public final void whenExecutingSameRequestsInDifferentThreads_thenExecuteRequest() throws InterruptedException, IOException {
HttpGet get = new HttpGet("http://www.baeldung.com"); HttpGet get = new HttpGet("http://www.baeldung.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom() CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager) .setConnectionManager(connManager)
.build(); .build();
MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client, get); MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client, get, connManager);
MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client, get); MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client, get, connManager);
MultiHttpClientConnThread thread3 = new MultiHttpClientConnThread(client, get); MultiHttpClientConnThread thread3 = new MultiHttpClientConnThread(client, get, connManager);
MultiHttpClientConnThread thread4 = new MultiHttpClientConnThread(client, get, connManager);
MultiHttpClientConnThread thread5 = new MultiHttpClientConnThread(client, get, connManager);
MultiHttpClientConnThread thread6 = new MultiHttpClientConnThread(client, get, connManager);
thread1.start(); thread1.start();
thread2.start(); thread2.start();
thread3.start(); thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread1.join(); thread1.join();
thread2.join(); thread2.join();
thread3.join(); thread3.join();
thread4.join();
thread5.join();
thread6.join();
client.close();
connManager.close();
} }
// Example 5.1. A Custom Keep Alive Strategy // Example 5.1. A Custom Keep Alive Strategy
@ -120,22 +136,19 @@ public class HttpClientConnectionManagementLiveTest {
public final void whenCustomizingKeepAliveStrategy_thenNoExceptions() { public final void whenCustomizingKeepAliveStrategy_thenNoExceptions() {
final ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { final ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
@Override @Override
public long getKeepAliveDuration(final HttpResponse myResponse, final HttpContext myContext) { public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) {
final HeaderElementIterator it = new BasicHeaderElementIterator(myResponse.headerIterator(HTTP.CONN_KEEP_ALIVE)); Args.notNull(response, "HTTP response");
while (it.hasNext()) { final Iterator<HeaderElement> it = MessageSupport.iterate(response, HeaderElements.KEEP_ALIVE);
final HeaderElement he = it.nextElement(); final HeaderElement he = it.next();
final String param = he.getName(); final String param = he.getName();
final String value = he.getValue(); final String value = he.getValue();
if ((value != null) && param.equalsIgnoreCase("timeout")) { if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000; try {
return TimeValue.ofSeconds(Long.parseLong(value));
} catch (final NumberFormatException ignore) {
} }
} }
final HttpHost target = (HttpHost) myContext.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); return TimeValue.ofSeconds(5);
if ("localhost".equalsIgnoreCase(target.getHostName())) {
return 10 * 1000;
} else {
return 5 * 1000;
}
} }
}; };
@ -144,42 +157,38 @@ public class HttpClientConnectionManagementLiveTest {
.setKeepAliveStrategy(myStrategy) .setKeepAliveStrategy(myStrategy)
.setConnectionManager(connManager) .setConnectionManager(connManager)
.build(); .build();
connManager.close();
} }
//Example 6.1. BasicHttpClientConnectionManager Connection Reuse //Example 6.1. BasicHttpClientConnectionManager Connection Reuse
@Test @Test
public final void givenBasicHttpClientConnManager_whenConnectionReuse_thenNoExceptions() throws IOException, HttpException, InterruptedException, ExecutionException { public final void givenBasicHttpClientConnManager_whenConnectionReuse_thenNoExceptions() throws InterruptedException, ExecutionException, TimeoutException, IOException, URISyntaxException {
BasicHttpClientConnectionManager basicConnManager = new BasicHttpClientConnectionManager(); BasicHttpClientConnectionManager connMgr = new BasicHttpClientConnectionManager();
HttpClientContext context = HttpClientContext.create();
// low level
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 443)); HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 443));
ConnectionRequest connRequest = basicConnManager.requestConnection(route, null); final HttpContext context = new BasicHttpContext();
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
basicConnManager.connect(conn, route, 1000, context);
basicConnManager.routeComplete(conn, route, context);
HttpRequestExecutor exeRequest = new HttpRequestExecutor(); final LeaseRequest connRequest = connMgr.lease("some-id", route, null);
context.setTargetHost((new HttpHost("www.baeldung.com", 80))); final ConnectionEndpoint endpoint = connRequest.get(Timeout.ZERO_MILLISECONDS);
HttpGet get = new HttpGet("http://www.baeldung.com"); connMgr.connect(endpoint, Timeout.ZERO_MILLISECONDS, context);
exeRequest.execute(get, conn, context);
basicConnManager.releaseConnection(conn, null, 1, TimeUnit.SECONDS); connMgr.release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
// high level
CloseableHttpClient client = HttpClients.custom() CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(basicConnManager) .setConnectionManager(connMgr)
.build(); .build();
client.execute(get); HttpGet httpGet = new HttpGet("https://www.example.com");
client.execute(httpGet, context, response -> response);
client.close();
connMgr.close();
} }
// Example 6.2. PoolingHttpClientConnectionManager: Re-Using Connections with Threads // Example 6.2. PoolingHttpClientConnectionManager: Re-Using Connections with Threads
@Test @Test
public final void whenConnectionsNeededGreaterThanMaxTotal_thenLeaseMasTotalandReuse() throws InterruptedException { public final void whenConnectionsNeededGreaterThanMaxTotal_thenLeaseMasTotalandReuse() throws InterruptedException, IOException {
HttpGet get = new HttpGet("http://echo.200please.com"); HttpGet get = new HttpGet("http://www.baeldung.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setDefaultMaxPerRoute(5); connManager.setDefaultMaxPerRoute(6);
connManager.setMaxTotal(5); connManager.setMaxTotal(6);
CloseableHttpClient client = HttpClients.custom() CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager) .setConnectionManager(connManager)
.build(); .build();
@ -193,48 +202,71 @@ public class HttpClientConnectionManagementLiveTest {
for (MultiHttpClientConnThread thread : threads) { for (MultiHttpClientConnThread thread : threads) {
thread.join(1000); thread.join(1000);
} }
client.close();
connManager.close();
} }
// Example 7.1. Setting Socket Timeout to 5 Seconds // Example 7.1. Setting Socket Timeout to 5 Seconds
@Test @Test
public final void whenConfiguringTimeOut_thenNoExceptions() { public final void whenConfiguringTimeOut_thenNoExceptions() throws ExecutionException, InterruptedException, TimeoutException, IOException {
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 80)); final HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 80));
try (PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager()) { final HttpContext context = new BasicHttpContext();
connManager.setSocketConfig(route.getTargetHost(), SocketConfig.custom() final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
.setSoTimeout(5000)
.build()); final ConnectionConfig connConfig = ConnectionConfig.custom()
assertTrue(connManager.getSocketConfig(route.getTargetHost()) .setSocketTimeout(5, TimeUnit.SECONDS)
.getSoTimeout() == 5000); .build();
}
connManager.setDefaultConnectionConfig(connConfig);
final LeaseRequest leaseRequest = connManager.lease("id1", route, null);
final ConnectionEndpoint endpoint = leaseRequest.get(Timeout.ZERO_MILLISECONDS);
connManager.connect(endpoint, null, context);
connManager.close();
} }
// Example 8.1. Setting the HttpClient to Check for Stale Connections // Example 8.1. Setting the HttpClient to Check for Stale Connections
@Test @Test
public final void whenHttpClientChecksStaleConns_thenNoExceptions() { public final void whenEvictIdealConn_thenNoExceptions() throws InterruptedException, IOException {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
HttpClients.custom() connManager.setMaxTotal(100);
.setDefaultRequestConfig(RequestConfig.custom() try (final CloseableHttpClient httpclient = HttpClients.custom()
.setStaleConnectionCheckEnabled(true)
.build())
.setConnectionManager(connManager) .setConnectionManager(connManager)
.build(); .evictExpiredConnections()
.evictIdleConnections(TimeValue.ofSeconds(2))
.build()) {
// create an array of URIs to perform GETs on
final String[] urisToGet = { "http://hc.apache.org/", "http://hc.apache.org/httpcomponents-core-ga/"};
for (final String requestURI : urisToGet) {
final HttpGet request = new HttpGet(requestURI);
System.out.println("Executing request " + request.getMethod() + " " + request.getRequestUri());
httpclient.execute(request, response -> {
System.out.println("----------------------------------------");
System.out.println(request + "->" + new StatusLine(response));
EntityUtils.consume(response.getEntity());
return null;
});
} }
// Example 8.2. Using a Stale Connection Monitor Thread final PoolStats stats1 = connManager.getTotalStats();
@Test System.out.println("Connections kept alive: " + stats1.getAvailable());
public final void whenCustomizedIdleConnMonitor_thenNoExceptions() throws InterruptedException {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); // Sleep 10 sec and let the connection evict or do its job
HttpClients.custom() Thread.sleep(4000);
.setConnectionManager(connManager)
.build(); final PoolStats stats2 = connManager.getTotalStats();
IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(connManager); System.out.println("Connections kept alive: " + stats2.getAvailable());
staleMonitor.start();
staleMonitor.join(1000); connManager.close();
}
} }
// Example 9.1. Closing Connection and Releasing Resources // Example 9.1. Closing Connection and Releasing Resources
@Test(expected = IllegalStateException.class) @Test
public final void whenClosingConnectionsandManager_thenCloseWithNoExceptions1() throws InterruptedException, ExecutionException, IOException, HttpException { public final void whenClosingConnectionsandManager_thenCloseWithNoExceptions1() throws IOException {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom() CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager) .setConnectionManager(connManager)
@ -246,16 +278,11 @@ public class HttpClientConnectionManagementLiveTest {
response.close(); response.close();
client.close(); client.close();
connManager.close(); connManager.close();
connManager.shutdown();
client.execute(get);
assertTrue(response.getEntity() == null);
} }
@Test @Test
// Example 3.2. TESTER VERSION // Example 3.2. TESTER VERSION
public final void whenTwoConnectionsForTwoRequests_thenTwoConnectionsAreLeased() throws InterruptedException { public final void whenTwoConnectionsForTwoRequests_thenTwoConnectionsAreLeased() throws InterruptedException, IOException {
HttpGet get1 = new HttpGet("https://www.baeldung.com"); HttpGet get1 = new HttpGet("https://www.baeldung.com");
HttpGet get2 = new HttpGet("https://www.google.com"); HttpGet get2 = new HttpGet("https://www.google.com");
@ -273,77 +300,11 @@ public class HttpClientConnectionManagementLiveTest {
thread2.start(); thread2.start();
thread1.join(); thread1.join();
thread2.join(1000); thread2.join(1000);
assertTrue(poolingConnManager.getTotalStats() Assert.assertTrue(poolingConnManager.getTotalStats()
.getLeased() == 2); .getLeased() == 2);
}
@Test client1.close();
// Example 4.2 Tester Version client2.close();
public final void whenExecutingSameRequestsInDifferentThreads_thenUseDefaultConnLimit() throws InterruptedException { poolingConnManager.close();
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
thread1.start();
thread2.start();
thread3.start();
thread1.join(10000);
thread2.join(10000);
thread3.join(10000);
}
@Test
// 6.2 TESTER VERSION
public final void whenConnectionsNeededGreaterThanMaxTotal_thenReuseConnections() throws InterruptedException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
poolingConnManager.setDefaultMaxPerRoute(5);
poolingConnManager.setMaxTotal(5);
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final MultiHttpClientConnThread[] threads = new MultiHttpClientConnThread[10];
int countConnMade = 0;
for (int i = 0; i < threads.length; i++) {
threads[i] = new MultiHttpClientConnThread(client, new HttpGet("http://www.baeldung.com/"), poolingConnManager);
}
for (final MultiHttpClientConnThread thread : threads) {
thread.start();
}
for (final MultiHttpClientConnThread thread : threads) {
thread.join(10000);
countConnMade++;
if (countConnMade == 0) {
assertTrue(thread.getLeasedConn() == 5);
}
}
}
@Test
@Ignore("Very Long Running")
// 8.2 TESTER VERSION
public final void whenCustomizedIdleConnMonitor_thenEliminateIdleConns() throws InterruptedException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(poolingConnManager);
final HttpGet get = new HttpGet("http://google.com");
final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
staleMonitor.start();
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
assertTrue(poolingConnManager.getTotalStats()
.getAvailable() == 1);
thread3.join(32000);
assertTrue(poolingConnManager.getTotalStats()
.getAvailable() == 0);
} }
} }

View File

@ -2,12 +2,12 @@ package com.baeldung.httpclient.conn;
import java.io.IOException; import java.io.IOException;
import org.apache.http.HttpResponse; import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.http.client.ClientProtocolException; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.http.client.methods.HttpGet; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.hc.core5.http.HttpEntity;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.hc.core5.http.HttpResponse;
import org.apache.http.util.EntityUtils; import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -45,22 +45,21 @@ public class MultiHttpClientConnThread extends Thread {
try { try {
logger.debug("Thread Running: " + getName()); logger.debug("Thread Running: " + getName());
logger.debug("Thread Running: " + getName());
if (connManager != null) { if (connManager != null) {
logger.info("Before - Leased Connections = " + connManager.getTotalStats().getLeased()); logger.info("Before - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("Before - Available Connections = " + connManager.getTotalStats().getAvailable()); logger.info("Before - Available Connections = " + connManager.getTotalStats().getAvailable());
} }
final HttpResponse response = client.execute(get); HttpEntity entity = client.execute(get).getEntity();
if (connManager != null) { if (connManager != null) {
leasedConn = connManager.getTotalStats().getLeased(); leasedConn = connManager.getTotalStats().getLeased();
logger.info("After - Leased Connections = " + connManager.getTotalStats().getLeased()); logger.info("After - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("After - Available Connections = " + connManager.getTotalStats().getAvailable()); logger.info("After - Available Connections = " + connManager.getTotalStats().getAvailable());
} }
EntityUtils.consume(entity);
EntityUtils.consume(response.getEntity());
} catch (final IOException ex) { } catch (final IOException ex) {
logger.error("", ex); logger.error("", ex);
} }

View File

@ -2,10 +2,9 @@ package com.baeldung.httpclient.conn;
import java.io.IOException; import java.io.IOException;
import org.apache.http.client.ClientProtocolException; import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.http.client.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,349 @@
package com.baeldung.httpclient.httpclient.conn;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpClientConnection;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ConnectionRequest;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.util.EntityUtils;
import org.junit.Ignore;
import org.junit.Test;
public class HttpClientConnectionManagementLiveTest {
// Example 2.1. Getting a Connection Request for a Low Level Connection (HttpClientConnection)
@Test
public final void whenLowLevelConnectionIsEstablished_thenNoExceptions() throws ConnectionPoolTimeoutException, InterruptedException, ExecutionException {
try (BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager()) {
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 80));
final ConnectionRequest connRequest = connManager.requestConnection(route, null);
assertNotNull(connRequest.get(1000, TimeUnit.SECONDS));
}
}
// Example 3.1. Setting the PoolingHttpClientConnectionManager on a HttpClient
@Test
public final void whenPollingConnectionManagerIsConfiguredOnHttpClient_thenNoExceptions() throws ClientProtocolException, IOException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
client.execute(new HttpGet("https://www.baeldung.com"));
assertTrue(poolingConnManager.getTotalStats()
.getLeased() == 1);
}
// Example 3.2. Using Two HttpClients to Connect to One Target Host Each
@Test
public final void whenTwoConnectionsForTwoRequests_thenNoExceptions() throws InterruptedException {
HttpGet get1 = new HttpGet("https://www.baeldung.com");
HttpGet get2 = new HttpGet("https://www.google.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client1 = HttpClients.custom()
.setConnectionManager(connManager)
.build();
CloseableHttpClient client2 = HttpClients.custom()
.setConnectionManager(connManager)
.build();
MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client1, get1);
MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client2, get2);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
assertTrue(connManager.getTotalStats()
.getLeased() == 0);
}
// Example 4.1. Increasing the Number of Connections that Can be Open and Managed Beyond the default Limits
@Test
public final void whenIncreasingConnectionPool_thenNoEceptions() {
try (PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager()) {
connManager.setMaxTotal(5);
connManager.setDefaultMaxPerRoute(4);
HttpHost host = new HttpHost("www.baeldung.com", 80);
connManager.setMaxPerRoute(new HttpRoute(host), 5);
}
}
// Example 4.2. Using Threads to Execute Connections
@Test
public final void whenExecutingSameRequestsInDifferentThreads_thenExecuteReuqest() throws InterruptedException {
HttpGet get = new HttpGet("http://www.baeldung.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.build();
MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client, get);
MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client, get);
MultiHttpClientConnThread thread3 = new MultiHttpClientConnThread(client, get);
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
}
// Example 5.1. A Custom Keep Alive Strategy
@Test
public final void whenCustomizingKeepAliveStrategy_thenNoExceptions() {
final ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(final HttpResponse myResponse, final HttpContext myContext) {
final HeaderElementIterator it = new BasicHeaderElementIterator(myResponse.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
final HeaderElement he = it.nextElement();
final String param = he.getName();
final String value = he.getValue();
if ((value != null) && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
final HttpHost target = (HttpHost) myContext.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
if ("localhost".equalsIgnoreCase(target.getHostName())) {
return 10 * 1000;
} else {
return 5 * 1000;
}
}
};
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.setConnectionManager(connManager)
.build();
}
// Example 6.1. BasicHttpClientConnectionManager Connection Reuse
@Test
public final void givenBasicHttpClientConnManager_whenConnectionReuse_thenNoExceptions() throws IOException, HttpException, InterruptedException, ExecutionException {
BasicHttpClientConnectionManager basicConnManager = new BasicHttpClientConnectionManager();
HttpClientContext context = HttpClientContext.create();
// low level
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 443));
ConnectionRequest connRequest = basicConnManager.requestConnection(route, null);
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
basicConnManager.connect(conn, route, 1000, context);
basicConnManager.routeComplete(conn, route, context);
HttpRequestExecutor exeRequest = new HttpRequestExecutor();
context.setTargetHost((new HttpHost("www.baeldung.com", 80)));
HttpGet get = new HttpGet("http://www.baeldung.com");
exeRequest.execute(get, conn, context);
basicConnManager.releaseConnection(conn, null, 1, TimeUnit.SECONDS);
// high level
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(basicConnManager)
.build();
client.execute(get);
}
// Example 6.2. PoolingHttpClientConnectionManager: Re-Using Connections with Threads
@Test
public final void whenConnectionsNeededGreaterThanMaxTotal_thenLeaseMasTotalandReuse() throws InterruptedException {
HttpGet get = new HttpGet("http://echo.200please.com");
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setDefaultMaxPerRoute(5);
connManager.setMaxTotal(5);
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.build();
MultiHttpClientConnThread[] threads = new MultiHttpClientConnThread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new MultiHttpClientConnThread(client, get, connManager);
}
for (MultiHttpClientConnThread thread : threads) {
thread.start();
}
for (MultiHttpClientConnThread thread : threads) {
thread.join(1000);
}
}
// Example 7.1. Setting Socket Timeout to 5 Seconds
@Test
public final void whenConfiguringTimeOut_thenNoExceptions() {
HttpRoute route = new HttpRoute(new HttpHost("www.baeldung.com", 80));
try (PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager()) {
connManager.setSocketConfig(route.getTargetHost(), SocketConfig.custom()
.setSoTimeout(5000)
.build());
assertTrue(connManager.getSocketConfig(route.getTargetHost())
.getSoTimeout() == 5000);
}
}
// Example 8.1. Setting the HttpClient to Check for Stale Connections
@Test
public final void whenHttpClientChecksStaleConns_thenNoExceptions() {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
HttpClients.custom()
.setDefaultRequestConfig(RequestConfig.custom()
.setStaleConnectionCheckEnabled(true)
.build())
.setConnectionManager(connManager)
.build();
}
// Example 8.2. Using a Stale Connection Monitor Thread
@Test
public final void whenCustomizedIdleConnMonitor_thenNoExceptions() throws InterruptedException {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
HttpClients.custom()
.setConnectionManager(connManager)
.build();
IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(connManager);
staleMonitor.start();
staleMonitor.join(1000);
}
// Example 9.1. Closing Connection and Releasing Resources
@Test(expected = IllegalStateException.class)
public final void whenClosingConnectionsandManager_thenCloseWithNoExceptions1() throws InterruptedException, ExecutionException, IOException, HttpException {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.build();
final HttpGet get = new HttpGet("http://google.com");
CloseableHttpResponse response = client.execute(get);
EntityUtils.consume(response.getEntity());
response.close();
client.close();
connManager.close();
connManager.shutdown();
client.execute(get);
assertTrue(response.getEntity() == null);
}
@Test
// Example 3.2. TESTER VERSION
public final void whenTwoConnectionsForTwoRequests_thenTwoConnectionsAreLeased() throws InterruptedException {
HttpGet get1 = new HttpGet("https://www.baeldung.com");
HttpGet get2 = new HttpGet("https://www.google.com");
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
final CloseableHttpClient client1 = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final CloseableHttpClient client2 = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client1, get1, poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client2, get2, poolingConnManager);
thread1.start();
thread2.start();
thread1.join();
thread2.join(1000);
assertTrue(poolingConnManager.getTotalStats()
.getLeased() == 2);
}
@Test
// Example 4.2 Tester Version
public final void whenExecutingSameRequestsInDifferentThreads_thenUseDefaultConnLimit() throws InterruptedException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, new HttpGet("http://www.google.com"), poolingConnManager);
thread1.start();
thread2.start();
thread3.start();
thread1.join(10000);
thread2.join(10000);
thread3.join(10000);
}
@Test
// 6.2 TESTER VERSION
public final void whenConnectionsNeededGreaterThanMaxTotal_thenReuseConnections() throws InterruptedException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
poolingConnManager.setDefaultMaxPerRoute(5);
poolingConnManager.setMaxTotal(5);
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final MultiHttpClientConnThread[] threads = new MultiHttpClientConnThread[10];
int countConnMade = 0;
for (int i = 0; i < threads.length; i++) {
threads[i] = new MultiHttpClientConnThread(client, new HttpGet("http://www.baeldung.com/"), poolingConnManager);
}
for (final MultiHttpClientConnThread thread : threads) {
thread.start();
}
for (final MultiHttpClientConnThread thread : threads) {
thread.join(10000);
countConnMade++;
if (countConnMade == 0) {
assertTrue(thread.getLeasedConn() == 5);
}
}
}
@Test
@Ignore("Very Long Running")
// 8.2 TESTER VERSION
public final void whenCustomizedIdleConnMonitor_thenEliminateIdleConns() throws InterruptedException {
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.build();
final IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(poolingConnManager);
final HttpGet get = new HttpGet("http://google.com");
final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager);
staleMonitor.start();
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
assertTrue(poolingConnManager.getTotalStats()
.getAvailable() == 1);
thread3.join(32000);
assertTrue(poolingConnManager.getTotalStats()
.getAvailable() == 0);
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.httpclient.httpclient.conn;
import java.util.concurrent.TimeUnit;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
public class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
IdleConnectionMonitorThread(final PoolingHttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
// API
@Override
public final void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(1000);
connMgr.closeExpiredConnections();
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (final InterruptedException ex) {
shutdown();
}
}
private void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}

View File

@ -0,0 +1,69 @@
package com.baeldung.httpclient.httpclient.conn;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MultiHttpClientConnThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final CloseableHttpClient client;
private final HttpGet get;
private PoolingHttpClientConnectionManager connManager;
private int leasedConn;
MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get, final PoolingHttpClientConnectionManager connManager) {
this.client = client;
this.get = get;
this.connManager = connManager;
leasedConn = 0;
}
MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get) {
this.client = client;
this.get = get;
}
// API
final int getLeasedConn() {
return leasedConn;
}
//
@Override
public final void run() {
try {
logger.debug("Thread Running: " + getName());
logger.debug("Thread Running: " + getName());
if (connManager != null) {
logger.info("Before - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("Before - Available Connections = " + connManager.getTotalStats().getAvailable());
}
final HttpResponse response = client.execute(get);
if (connManager != null) {
leasedConn = connManager.getTotalStats().getLeased();
logger.info("After - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("After - Available Connections = " + connManager.getTotalStats().getAvailable());
}
EntityUtils.consume(response.getEntity());
} catch (final IOException ex) {
logger.error("", ex);
}
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.httpclient.httpclient.conn;
import java.io.IOException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
public class TesterVersion_MultiHttpClientConnThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final CloseableHttpClient client;
private final HttpGet get;
private PoolingHttpClientConnectionManager connManager;
TesterVersion_MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get, final PoolingHttpClientConnectionManager connManager) {
this.client = client;
this.get = get;
this.connManager = Preconditions.checkNotNull(connManager);
}
//
@Override
public final void run() {
try {
logger.debug("Thread Running: " + getName());
logger.info("Before - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("Before - Available Connections = " + connManager.getTotalStats().getAvailable());
client.execute(get);
logger.info("After - Leased Connections = " + connManager.getTotalStats().getLeased());
logger.info("After - Available Connections = " + connManager.getTotalStats().getAvailable());
} catch (final IOException ex) {
logger.error("", ex);
}
}
}