Apache AsyncHttpClient 4.1.4 创建新的套接字连接而不是重用来自连接池的连接
Apache AsyncHttpClient 4.1.4 creating new socket connection instead of reusing connection from Connection pool
我们正在使用具有以下依赖项的 Apache AsyncHttpClient
[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
我们注意到它正在为每个请求创建与下游(启用 ssl 和客户端身份验证)的新套接字连接。我们的期望是重用池中的连接。
以下是调试日志
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
注意:在没有 SSL 的情况下使用时,它按照我们的预期工作(重用池中的连接)
以下是我们的配置
"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": "**"
以下是要求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)
以下为回复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]"
注:jdk版本为1.8
Apache HttpClient 区分所谓的无状态和有状态连接。有状态连接是使用特定用户身份或安全上下文创建的连接。 HttpClient 版本 4.x 和 5.x 默认支持两种类型:使用 NTLM 身份验证的连接和使用客户端身份验证的 TLS 连接。完全可重用的有状态连接被认为是敏感的。连接池管理器不会租用这些连接,除非 HTTP 执行上下文中的用户令牌与连接池中保持活动状态的连接状态相匹配。
在同一执行上下文中执行多个请求,从而使它们共享相同的用户身份。 重要:HttpContext
个实例不得同时使用! HttpContext
中存储的某些属性不是线程安全的!
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();
}
对相关请求使用不同的 HttpContext
实例,但手动为这些请求分配相同的用户令牌
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();
}
如果客户端不支持不同的用户身份(所有 HTTP 请求共享一个公共安全上下文),可以简单地禁用连接状态管理
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
.disableConnectionState()
.build();
我们正在使用具有以下依赖项的 Apache AsyncHttpClient
[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
我们注意到它正在为每个请求创建与下游(启用 ssl 和客户端身份验证)的新套接字连接。我们的期望是重用池中的连接。
以下是调试日志
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
注意:在没有 SSL 的情况下使用时,它按照我们的预期工作(重用池中的连接)
以下是我们的配置
"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": "**"
以下是要求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)
以下为回复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]"
注:jdk版本为1.8
Apache HttpClient 区分所谓的无状态和有状态连接。有状态连接是使用特定用户身份或安全上下文创建的连接。 HttpClient 版本 4.x 和 5.x 默认支持两种类型:使用 NTLM 身份验证的连接和使用客户端身份验证的 TLS 连接。完全可重用的有状态连接被认为是敏感的。连接池管理器不会租用这些连接,除非 HTTP 执行上下文中的用户令牌与连接池中保持活动状态的连接状态相匹配。
在同一执行上下文中执行多个请求,从而使它们共享相同的用户身份。 重要:
HttpContext
个实例不得同时使用!HttpContext
中存储的某些属性不是线程安全的!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(); }
对相关请求使用不同的
HttpContext
实例,但手动为这些请求分配相同的用户令牌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(); }
如果客户端不支持不同的用户身份(所有 HTTP 请求共享一个公共安全上下文),可以简单地禁用连接状态管理
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() .disableConnectionState() .build();