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场景,主要诱因有两个:
- 默认连接池配置过小:RestTemplate默认使用的HttpClient连接池,最大连接数、单路由最大连接数设置偏低,大Payload请求处理耗时更长,连接占用久,导致新请求无法获取空闲连接。
- 连接泄漏:请求异常场景下连接未正确归还池,或者代码存在其他导致连接无法释放的逻辑,最终耗尽池资源。另外代码中重复添加HttpMessageConverter的操作虽不直接引发超时,但会导致单例RestTemplate的转换器列表膨胀,引发潜在序列化问题。
具体解决步骤
自定义连接池配置
重新配置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可适当延长以减少超时概率。排查并修复连接泄漏
利用HttpClient连接池的监控方法(如connectionManager.getTotalStats())查看连接占用情况,确认是否存在泄漏。RestTemplate默认会自动释放连接,但需确保异常处理逻辑未阻止连接正常归还。修复转换器重复添加问题
针对单例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
相关产品推荐
相关产品推荐

