在超图下载瓦片时,报错org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool。
任务池有1W多瓦片需要下载,连接池我设置的是500最大连接数。代码里也对资源释放了如下图。为什么还是报无连接数可用,需要怎么修改?




在超图下载瓦片时,报错org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool。
任务池有1W多瓦片需要下载,连接池我设置的是500最大连接数。代码里也对资源释放了如下图。为什么还是报无连接数可用,需要怎么修改?




晚上好🌙🌙🌙
本答案参考通义千问
在使用 Java 多线程下载大量瓦片(如超图的瓦片服务)时,遇到 org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool 错误,说明连接池中没有可用的连接,即使你设置了最大连接数为 500。这通常意味着:
你设置的最大连接数是 500,但任务池中有 1W 多个瓦片需要下载。如果每个线程都试图获取一个连接,而连接池只能提供 500 个连接,那么超过这个数量的线程会阻塞等待,最终导致超时。
重点:线程数 > 连接池最大连接数 = 会导致连接池耗尽。
即使代码中对资源进行了释放,也可能因为以下原因导致连接未被回收:
CloseableHttpResponseHttpClientPoolingHttpClientConnectionManager 没有正确配置)如果你使用的是 ExecutorService 创建了大量线程(例如 1000+),而连接池只有 500 个连接,就会出现“线程多于连接”的情况。
确保线程池大小不超过连接池最大连接数,或者采用 线程池 + 信号量(Semaphore) 来控制并发数。
Semaphore 控制并发):import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class TileDownloader {
private static final int MAX_CONNECTIONS = 500;
private static final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1000); // 线程池可以更大,由信号量控制
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
try {
semaphore.acquire(); // 获取许可,最多同时执行 MAX_CONNECTIONS 个
downloadTile(); // 下载瓦片逻辑
} finally {
semaphore.release(); // 释放许可
}
});
}
executor.shutdown();
}
private static void downloadTile() {
// 实际的 HTTP 请求逻辑
// 确保关闭 HttpClient 和响应对象
}
}
重点:通过
Semaphore控制实际并发数,避免超过连接池容量。
确保每次请求后都正确关闭 CloseableHttpResponse、HttpClient,并使用 try-with-resources。
try-with-resources):import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class TileDownloader {
public static void downloadTile(String url) {
try (CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(new HttpGet(url))) {
if (response.getStatusLine().getStatusCode() == 200) {
String content = EntityUtils.toString(response.getEntity());
// 处理返回内容
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
重点:使用 try-with-resources 确保资源自动关闭,防止连接泄漏。
确保你的 PoolingHttpClientConnectionManager 配置合理,包括最大连接数、超时时间等。
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
public class HttpClientUtil {
private static final int MAX_TOTAL_CONNECTIONS = 500;
private static final int DEFAULT_PER_ROUTE = 200;
public static CloseableHttpClient createClient() {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
connManager.setDefaultMaxPerRoute(DEFAULT_PER_ROUTE);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(5000)
.build();
return HttpClients.custom()
.setConnectionManager(connManager)
.setRequestConfig(requestConfig)
.build();
}
}
重点:配置合理的连接池参数,避免连接泄漏和超时。
如果你确实需要更高的并发能力,可以适当增加连接池的 maxTotal 和 defaultMaxPerRoute,但注意不要超出系统或服务器的承受范围。
connManager.setMaxTotal(1000); // 最大连接数
connManager.setDefaultMaxPerRoute(500); // 每个路由最大连接数
重点:根据实际硬件和网络环境调整连接池大小。
| 问题 | 解决方案 |
|------|----------|
| 连接池资源耗尽 | 控制线程数,使用 Semaphore 或限制线程池大小 |
| 资源未正确释放 | 使用 try-with-resources 确保关闭 HttpClient 和 HttpResponse |
| 连接池配置不合理 | 调整 PoolingHttpClientConnectionManager 的 setMaxTotal 和 setDefaultMaxPerRoute |
| 并发线程过多 | 限制线程池大小,避免超过连接池容量 |
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.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class TileDownloader {
private static final int MAX_CONNECTIONS = 500;
private static final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1000); // 线程池可稍大
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
try {
semaphore.acquire(); // 控制并发
downloadTile("http://tileserver.com/tile/" + i);
} finally {
semaphore.release(); // 释放许可
}
});
}
executor.shutdown();
}
private static void downloadTile(String url) {
try (CloseableHttpClient httpClient = createClient();
CloseableHttpResponse response = httpClient.execute(new HttpGet(url))) {
if (response.getStatusLine().getStatusCode() == 200) {
String content = EntityUtils.toString(response.getEntity());
// 处理返回内容
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static CloseableHttpClient createClient() {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(500);
connManager.setDefaultMaxPerRoute(200);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(5000)
.build();
return HttpClients.custom()
.setConnectionManager(connManager)
.setRequestConfig(requestConfig)
.build();
}
}
如需进一步优化性能,还可以考虑:
如需我帮助你集成这些功能,也可以继续提问!