P37 RestTemplate 如何优化连接池
面试题:Spring 的 RestTemplate 调用第三方接口很慢/连接不够用,怎么优化?
1. 问题根源
RestTemplate 默认基于 SimpleClientHttpRequestFactory(JDK HttpURLConnection),不维护连接池,每次请求都新建连接:
- 频繁 TCP 建连/断开,握手开销大;
- 高并发下连接被占满/超时;
- 没有连接复用,性能差。
2. 方案:换成 Apache HttpClient / OkHttp 并配置连接池
① 引入依赖
xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>② 配置连接池并注入 RestTemplate
java
@Bean
public RestTemplate restTemplate() {
// 连接池管理
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // 总连接数
cm.setDefaultMaxPerRoute(50); // 单路由(单个目标域名)最大连接数
// 请求配置:超时
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(3000) // 建连超时
.setSocketTimeout(5000) // 读超时
.setConnectionRequestTimeout(3000) // 从连接池取连接超时
.build();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(config)
.evictIdleConnections(60, TimeUnit.SECONDS) // 回收空闲连接
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(client);
return new RestTemplate(factory);
}3. 关键参数与注意点
| 参数 | 作用 |
|---|---|
maxTotal | 连接池总连接数上限 |
maxPerRoute | 同一目标主机最大连接数(更关键,通常先调它) |
| 建连/读/取连接超时 | 防止请求无限挂起 |
| 空闲连接回收 | 防止服务端断开后客户端用死连接 |
注意:
maxPerRoute设太小,高并发下请求在连接池排队(ConnectionRequestTimeout 触发);- 与目标服务端的最大连接数匹配,否则压满对方;
- 连接池满了可以等待,但一定要配超时,避免线程耗尽。
4. 进一步优化
- 升级 WebClient(响应式):非阻塞 IO,同样的连接数能支撑更高并发;
- 接口是 IO 密集的,调用线程池与连接池比例要匹配;
- 对第三方调用做超时、重试、熔断(如配合 Resilience4j/Sentinel);
- 连接可用性校验(
setValidateAfterInactivity)防止陈旧连接。
一句话总结
RestTemplate 默认不复用连接;改用 Apache HttpClient/OkHttp + PoolingHttpClientConnectionManager 配置连接池(maxTotal/maxPerRoute)、合理超时和空闲回收,并发能力大幅提升;追求更高吞吐可换 WebClient。