Skip to content

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。

基于 VitePress 重建