Spring Boot项目遇Too many open files异常,排查RestTemplate配置问题
问题分析:RestTemplate导致"Too many open files"错误
问题描述
在基于Spring Boot的生产环境项目中,调用同网络内其他微服务的REST API时出现错误:Too many open files; nested exception is java.net.SocketException: Too many open files。近期为RestTemplate添加超时配置后触发该问题,当前每个请求都会调用指定方法创建RestTemplate实例,而使用单例实例时无此问题,请问该配置是否存在问题?
环境信息:Spring Boot 1.5.8,Java 1.8,Linux操作系统
报错堆栈
Too many open files; nested exception is java.net.SocketException: Too many open files java.net.SocketException: Too many open files at java.net.Socket.createImpl(Socket.java:460) at java.net.Socket.connect(Socket.java:587) at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:673) at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) at sun.net.NetworkClient.doConnect(NetworkClient.java:180) at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264) at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
当前RestTemplate配置代码
public RestTemplate getRestTemplate(int timeoutMs) { RestTemplate restTemplate = restTemplateBuilder.build(); SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory(); clientHttpRequestFactory.setConnectTimeout(timeoutMs); clientHttpRequestFactory.setReadTimeout(timeoutMs); restTemplate.setRequestFactory(clientHttpRequestFactory); restTemplate.setInterceptors(Collections.singletonList(appClientInterceptor)); restTemplate.setErrorHandler(appClientErrorHanlder); return restTemplate; }
问题根源
- 你的配置存在明显问题:每次请求都创建新的RestTemplate实例,而每个RestTemplate实例会关联独立的
SimpleClientHttpRequestFactory,后者底层使用JDK自带的HttpURLConnection,默认情况下HttpURLConnection的连接池是每个实例独立维护的。 - 频繁创建RestTemplate实例会导致大量未及时释放的Socket连接堆积,超过Linux系统的文件句柄上限,触发"Too many open files"错误。
- 单例模式下,RestTemplate复用同一个连接池,连接会被合理复用和回收,不会出现句柄耗尽的情况。
解决方案
改为单例RestTemplate实例:
如果需要不同的超时配置,不要创建新实例,而是通过自定义ClientHttpRequestFactory或者使用拦截器动态调整超时;或者针对不同超时需求创建少量几个单例实例,而非每次请求都新建。示例单例配置(固定超时):@Bean public RestTemplate restTemplate(@Value("${rest.timeout:5000}") int timeoutMs) { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(timeoutMs); factory.setReadTimeout(timeoutMs); RestTemplate restTemplate = new RestTemplate(factory); restTemplate.setInterceptors(Collections.singletonList(appClientInterceptor)); restTemplate.setErrorHandler(appClientErrorHanlder); return restTemplate; }替换为更高效的请求工厂:
推荐使用HttpComponentsClientHttpRequestFactory(基于Apache HttpClient),它的连接池管理更成熟,即使不小心创建多个实例,资源泄漏的风险也更低,但仍建议单例使用。示例配置:@Bean public RestTemplate restTemplate(@Value("${rest.timeout:5000}") int timeoutMs) { HttpClient httpClient = HttpClientBuilder.create() .setConnectionTimeToLive(timeoutMs, TimeUnit.MILLISECONDS) .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); factory.setConnectTimeout(timeoutMs); factory.setReadTimeout(timeoutMs); RestTemplate restTemplate = new RestTemplate(factory); restTemplate.setInterceptors(Collections.singletonList(appClientInterceptor)); restTemplate.setErrorHandler(appClientErrorHanlder); return restTemplate; }临时应急调整:
可以临时调高Linux系统的文件句柄限制(修改/etc/security/limits.conf),但这只是治标不治本,无法解决根本的资源泄漏问题。
内容的提问来源于stack exchange,提问作者Prashant Thorat
相关产品推荐
相关产品推荐

