We are using Apache AsyncHttpClient having below dependencies
[INFO] +- org.apache.httpcomponents:httpasyncclient:jar:4.1.4:compile
[INFO] | +- org.apache.httpcomponents:httpcore:jar:4.4.10:compile
[INFO] | +- org.apache.httpcomponents:httpcore-nio:jar:4.4.10:compile
[INFO] | \- org.apache.httpcomponents:httpclient:jar:4.5.6:compile
We are noticing it is creating a new socket connection with downstream(having ssl and client auth enabled)for every request. Our expectation is to reuse the connection from pool.
Following are the debug logs
2020:03:31:19:49:27.430 DEBUG I/O dispatcher 8 ManagedNHttpClientConnectionImpl http-outgoing-55 127.0.0.1:51706<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.430 DEBUG I/O dispatcher 9 ManagedNHttpClientConnectionImpl http-outgoing-56 127.0.0.1:51740<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.534 DEBUG I/O dispatcher 10 ManagedNHttpClientConnectionImpl http-outgoing-57 127.0.0.1:51741<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.637 DEBUG I/O dispatcher 11 ManagedNHttpClientConnectionImpl http-outgoing-58 127.0.0.1:51742<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.740 DEBUG I/O dispatcher 12 ManagedNHttpClientConnectionImpl http-outgoing-59 127.0.0.1:51743<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.840 DEBUG I/O dispatcher 13 ManagedNHttpClientConnectionImpl http-outgoing-60 127.0.0.1:51744<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:32.938 DEBUG I/O dispatcher 14 ManagedNHttpClientConnectionImpl http-outgoing-61 127.0.0.1:51745<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.041 DEBUG I/O dispatcher 15 ManagedNHttpClientConnectionImpl http-outgoing-62 127.0.0.1:51746<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.146 DEBUG I/O dispatcher 16 ManagedNHttpClientConnectionImpl http-outgoing-63 127.0.0.1:51747<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.238 DEBUG I/O dispatcher 1 ManagedNHttpClientConnectionImpl http-outgoing-64 127.0.0.1:51748<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.344 DEBUG I/O dispatcher 2 ManagedNHttpClientConnectionImpl http-outgoing-65 127.0.0.1:51749<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.432 DEBUG I/O dispatcher 3 ManagedNHttpClientConnectionImpl http-outgoing-66 127.0.0.1:51750<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.541 DEBUG I/O dispatcher 4 ManagedNHttpClientConnectionImpl http-outgoing-67 127.0.0.1:51751<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.638 DEBUG I/O dispatcher 5 ManagedNHttpClientConnectionImpl http-outgoing-68 127.0.0.1:51752<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
2020:03:31:19:51:33.743 DEBUG I/O dispatcher 6 ManagedNHttpClientConnectionImpl http-outgoing-69 127.0.0.1:51753<->127.0.0.1:8102[ACTIVE][rw:][ACTIVE][rw][NEED_UNWRAP][0][0][239]: Set timeout 500
Note: When used without SSL, it is working as per our expectation(reusing the connection from pool)
Following are our configs
"sslEnabled": true,
"host": "127.0.0.1",
"port": 8102,
"staleConnectionMonitorThreadName": "http-stale-connection-cleaner-thread",
"publishMaxThreadPoolSize": 1,
"defaultMaxThreadsPerRoute": 1,
"maxThreadsPerRoute": 1,
"connectionRequestTimeoutMs": 500,
"connectionTimeoutMs": 500,
"socketTimeoutMs": 500,
"evictThreadSleepTimeMs" : 5000,
"maxKeepAliveTimeMs" : 30000,
"trustStorePath": "abc.jks",
"trustStoreKey": "**",
"keyStorePath": "xyx.jks",
"keyStoreKey": "**"
Following are request headers
Content-Type: application/json
v-c-correlation-id: f7e046a9-e1f1-44f1-9fb4-dddcb973a951
v-c-username: xyz
Content-Length: 218
Host: 127.0.0.1:8102
Connection: Keep-Alive
User-Agent: Apache-HttpAsyncClient/4.1.4 (Java/1.8.0_201)
Following are response headers
"HTTP/1.1 200 OK[\r][\n]"
"Content-Type: application/json[\r][\n]"
"v-c-correlation-id: f7e046a9-e1f1-44f1-9fb4-dddcb973a951[\r][\n]"
"Content-Length: 0[\r][\n]"
Note: jdk version is 1.8
Apache HttpClient differentiate so called state-less and state-ful connections. Stateful connections are those that get created with a particular user identity or a security context. Two types are supported by HttpClient version 4.x and 5.x by default: connections with NTLM authentication and TLS connections with client authentication. Stateful connections while perfectly re-usable are considered sensitive. The connection pool manager will not lease those connections unless the user token in the HTTP execution context matches that the state of the connection kept alive in the pool.
One execute multiple requests within the same execution context and thus make them share the same user identity. IMPORTANT: HttpContext
instances MUST NOT be used concurrently! Some attributes stored in HttpContext
are not threading safe!
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
try {
httpclient.start();
HttpClientContext clientContext = HttpClientContext.create();
HttpGet request1 = new HttpGet("http://httpbin.org/get");
Future<HttpResponse> future1 = httpclient.execute(request1, clientContext, null);
HttpResponse response1 = future1.get();
System.out.println("Response: " + response1.getStatusLine());
HttpGet request2 = new HttpGet("http://httpbin.org/get");
Future<HttpResponse> future2 = httpclient.execute(request2, clientContext, null);
HttpResponse response2 = future2.get();
System.out.println("Response: " + response2.getStatusLine());
} finally {
System.out.println("Shutting down");
httpclient.close();
}
Use different HttpContext
instances for related requests but manually assign the same user token to those requests
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
try {
httpclient.start();
HttpClientContext clientContext1 = HttpClientContext.create();
clientContext1.setUserToken("appuser");
HttpGet request1 = new HttpGet("http://httpbin.org/get");
Future<HttpResponse> future1 = httpclient.execute(request1, clientContext1, null);
HttpResponse response1 = future1.get();
System.out.println("Response: " + response1.getStatusLine());
HttpClientContext clientContext2 = HttpClientContext.create();
clientContext2.setUserToken("appuser");
HttpGet request2 = new HttpGet("http://httpbin.org/get");
Future<HttpResponse> future2 = httpclient.execute(request2, clientContext2, null);
HttpResponse response2 = future2.get();
System.out.println("Response: " + response2.getStatusLine());
} finally {
System.out.println("Shutting down");
httpclient.close();
}
If the client endpoint does not support different user identities (there is a common security context shared by all HTTP requests) one can simply disable connection state management
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
.disableConnectionState()
.build();