You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Standard HttpClient异步请求性能瓶颈排查与调优咨询

问题场景

我执行了一个包含100次循环的逻辑,向同一台测试Web服务器发起异步请求,该服务器会延迟3秒后返回响应。

第一个实现代码:

HttpClient httpClient = HttpClient.newBuilder().build();
for (int i = 0; i < 100; i++) {
    HttpRequest request = HttpRequest.newBuilder()
            .uri(uri)
            .POST(HttpRequest.BodyPublishers.ofString("Hello"))
            .build();

    CompletableFuture<?> result = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenAccept(r -> {
                System.out.printf(
                        "[%s] Thread: %s, Status: %s, Body: %s%n",
                        Instant.now(), Thread.currentThread().getName(), r.statusCode(), r.body()
                );
            });
}

上述代码执行耗时约19秒,将并行度设置在2-11区间(我的机器默认值为11)几乎没有效果。

但如果创建另一个使用相同默认ForkJoinPool的客户端,交替发送请求:

HttpClient httpClient1 = HttpClient.newBuilder().build();
HttpClient httpClient2 = HttpClient.newBuilder().build();
for (int i = 0; i < 100; i++) {
    HttpRequest request = HttpRequest.newBuilder()
            .uri(uri)
            .POST(HttpRequest.BodyPublishers.ofString("Hello"))
            .build();

    HttpClient httpClient = i % 2 == 0 ? httpClient1 : httpClient2;

    CompletableFuture<?> result = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenAccept(r -> {
                System.out.printf(
                        "[%s] Thread: %s, Status: %s, Body: %s%n",
                        Instant.now(), Thread.currentThread().getName(), r.statusCode(), r.body()
                );
            });
}

代码仅需12秒即可执行完成,从日志中可以看到,两个客户端使用相同的线程执行任务。

疑问

在第一个场景中我触碰到了什么限制?是否是每路由连接数(connections per route)?能否通过JVM参数或其他方式进行调整?


解答

1. 触发的限制

你碰到的确实是每路由最大连接数限制。Java 11引入的HttpClient默认使用内置连接池,针对同一目标路由(同一主机+端口组合)的最大并发连接数默认值为5。单个HttpClient实例同时只能向目标服务器发起5个请求,剩余请求需等待已有连接释放后才能执行,这直接导致了请求处理的串行化瓶颈,耗时远超理想并行时间。

2. 双客户端更快的原因

每个HttpClient实例拥有独立的连接池,两个实例总共能提供10个并发连接到目标路由,并行度提升一倍,因此整体耗时大幅降低。虽然它们共享同一个ForkJoinPool处理回调,但连接池是实例级别的,不影响连接并发数。

3. 调整方案

代码层面自定义连接池(推荐)

通过HttpClient.Builder的connectionPool方法自定义连接池参数,直接修改每路由最大连接数:

// 创建自定义连接池,设置每路由最大连接数为20,总连接数为200
HttpClientConnectionPool connectionPool = HttpClientConnectionPool.newBuilder()
        .maxConnectionsPerRoute(20)
        .maxConnections(200)
        .build();

HttpClient httpClient = HttpClient.newBuilder()
        .connectionPool(connectionPool)
        .build();

JVM参数调整说明

Java的HttpClient没有直接的JVM参数用于修改默认连接池配置,必须通过代码自定义连接池来调整参数。


内容的提问来源于stack exchange,提问作者Alterant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:13:26