我们正在从 Apache HttpClient 4.5 升级到 5.2,池超时的工作方式似乎发生了重大变化。我们希望配置池,使其具有固定数量的可用连接,但其他线程根本不等待。由于非正值被视为无限超时,因此我们将 ConnectionRequestTimeout 设置为 1 毫秒,以便它检查空闲连接,如果池耗尽,它几乎会立即失败。
在 4.5 中,这种方法工作得很好,但在 5.2 中,似乎过早地检查了超时,并且在应该仍然有可用连接时失败了。这是一个可以相当可靠地为我重现问题的测试:
class Test {
static CloseableHttpClient client;
public static void main(String[] args) {
var requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofMilliseconds(1))
.build();
var connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(50);
connManager.setDefaultMaxPerRoute(50);
client = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connManager)
.build();
for (int i = 0; i < 45; i++) {
new Requester().start();
}
}
static class Requester extends Thread {
String result;
@Override
public void run() {
try {
result = client.execute(new HttpGet("https://www.example.com"), ClassicHttpResponse::toString);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这会输出一系列堆栈跟踪,如下所示:
org.apache.hc.client5.http.impl.classic.RequestFailedException: Request execution failed
at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.acquireEndpoint(InternalExecRuntime.java:131)
at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:125)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:113)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:152)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:116)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:170)
at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:245)
at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:188)
at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:162)
at Test$Requester.run(Test.java:40)
Caused by: org.apache.hc.core5.util.DeadlineTimeoutException: Deadline: 2024-01-10T02:02:37.621+0000, -15 MILLISECONDS overdue
at org.apache.hc.core5.util.DeadlineTimeoutException.from(DeadlineTimeoutException.java:49)
at org.apache.hc.core5.pool.StrictConnPool.lease(StrictConnPool.java:222)
at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.lease(PoolingHttpClientConnectionManager.java:298)
at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.acquireEndpoint(InternalExecRuntime.java:103)
... 15 more
将超时增加到 25 毫秒可以减少超时的频率,但偶尔也会发生。这是一个错误吗?是否有另一种方法可以配置具有低(或无)超时的池?
为什么要使用连接超时来实现它? 在 HttpClient 上使用它 .evictIdleConnections(Timeout.ofMilliseconds(1))