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

RestTemplate抛出ConnectionPoolTimeoutException问题求助

问题:RestTemplate发送大Payload时出现连接池超时错误

我尝试用RestTemplate发送4-5MB大小的Payload进行POST请求,部分请求能成功,但有部分请求还未到达API网关就报错:

org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

我的RestTemplate实现代码

@Component
public class RestTemplateHelper {
    @Autowired
    private RestTemplate restTemplate;

    public RestTemplateHelper() {
    }

    private <X, Y> Y post(final String url, final String accessToken, final MediaType mediaType, final X requestBody, final Class<Y> responseType, final Map<String, String> extraHeaders, HttpMessageConverter httpMessageConverter, RestTemplate restTemplateFromParam) {
        HttpHeaders headers = this.getHeaders(accessToken, mediaType, extraHeaders);
        HttpEntity<X> httpEntity = new HttpEntity(requestBody, headers);

        ResponseEntity responseEntity;
        NonRetriableException nonRetriableException;
        try {
            RestTemplate restTemplateToUse = this.restTemplate;
            if (Objects.nonNull(httpMessageConverter)) {
                restTemplateToUse = Objects.nonNull(restTemplateFromParam) ? restTemplateFromParam : restTemplateToUse;
                restTemplateToUse.getMessageConverters().add(0, httpMessageConverter);
            }

            responseEntity = restTemplateToUse.exchange(url, HttpMethod.POST, httpEntity, responseType, new Object[0]);
        } catch (ResourceAccessException var14) {
            nonRetriableException = new NonRetriableException(var14);
            nonRetriableException.setRootException(var14);
            throw nonRetriableException;
        } catch (RestClientResponseException var15) {
            nonRetriableException = new NonRetriableException(var15);
            nonRetriableException.setRawStatusCode(var15.getRawStatusCode());
            nonRetriableException.setStatusText(var15.getStatusText());
            nonRetriableException.setResponseBody(var15.getResponseBodyAsString());
            nonRetriableException.setResponseHeaders(var15.getResponseHeaders());
            nonRetriableException.setRootException(var15);
            throw nonRetriableException;
        } catch (Exception var16) {
            throw new NonRetriableException(var16);
        }

        return responseEntity.getBody();
    }
}

问题原因与解决办法

核心原因

这个错误本质是连接池中的可用连接被耗尽,请求等待获取连接超时。结合大Payload场景,主要诱因有两个:

  1. 默认连接池配置过小:RestTemplate默认使用的HttpClient连接池,最大连接数、单路由最大连接数设置偏低,大Payload请求处理耗时更长,连接占用久,导致新请求无法获取空闲连接。
  2. 连接泄漏:请求异常场景下连接未正确归还池,或者代码存在其他导致连接无法释放的逻辑,最终耗尽池资源。另外代码中重复添加HttpMessageConverter的操作虽不直接引发超时,但会导致单例RestTemplate的转换器列表膨胀,引发潜在序列化问题。

具体解决步骤

  1. 自定义连接池配置
    重新配置RestTemplate的HttpClient连接池,增大连接数阈值,同时设置合理的超时参数:

    @Bean
    public RestTemplate restTemplate() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        // 总最大连接数
        connectionManager.setMaxTotal(200);
        // 单路由最大连接数
        connectionManager.setDefaultMaxPerRoute(50);
        
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000) // 连接建立超时
                .setConnectionRequestTimeout(10000) // 连接池等待超时,对应报错场景
                .setSocketTimeout(30000) // 数据读取超时
                .build();
    
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .build();
    
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(factory);
    }
    

    可根据业务并发量调整setMaxTotal和setDefaultMaxPerRoute的值,setConnectionRequestTimeout可适当延长以减少超时概率。

  2. 排查并修复连接泄漏
    利用HttpClient连接池的监控方法(如connectionManager.getTotalStats())查看连接占用情况,确认是否存在泄漏。RestTemplate默认会自动释放连接,但需确保异常处理逻辑未阻止连接正常归还。

  3. 修复转换器重复添加问题
    针对单例RestTemplate,避免重复添加同类型转换器:

    if (Objects.nonNull(httpMessageConverter)) {
        restTemplateToUse = Objects.nonNull(restTemplateFromParam) ? restTemplateFromParam : restTemplateToUse;
        // 先移除同类型转换器,再添加新实例
        restTemplateToUse.getMessageConverters().removeIf(converter -> converter.getClass().equals(httpMessageConverter.getClass()));
        restTemplateToUse.getMessageConverters().add(0, httpMessageConverter);
    }
    

内容的提问来源于stack exchange,提问作者Ghulam Moinul Quadir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 07:35:26