Кейс: под падал с too many open files из-за RestTemplate
Сервис ходил в соседний backend через RestTemplate, поднятый через new RestTemplate() без явного HTTP-клиента. Под нагрузкой в несколько сотен RPS под начал падать с IOException: too many open files, хотя дескрипторов на подключения к БД хватало с запасом.
Дефолтный RestTemplate использует SimpleClientHttpRequestFactory, который открывает новое TCP-соединение на каждый запрос через HttpURLConnection и не держит connection pool. Каждое соединение после ответа уходит в TIME_WAIT на уровне ОС вместо повторного использования — при высоком RPS сокеты накапливаются быстрее, чем система их закрывает.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(200); cm.setDefaultMaxPerRoute(50);
CloseableHttpClient client = HttpClients.custom() .setConnectionManager(cm) .build();
RestTemplate restTemplate = new RestTemplate( new HttpComponentsClientHttpRequestFactory(client) );
Спросят следом: почему дескрипторы кончились именно на этом сервисе, а не глобально по ОС? ulimit -n считается на процесс, и под с дефолтным лимитом в контейнере упирается в потолок быстрее, чем кажется по логам приложения. Ещё уточнят про keep-alive: без него соединение закрывается после каждого ответа, и проблема с TIME_WAIT становится ещё острее при частых запросах.
RestTemplate по умолчанию не пул, а фабрика новых соединений — тот же паттерн, что и SimpleAsyncTaskExecutor у @Async.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки