У меня есть служба RESTful, которая работает очень быстро. Я тестирую его на localhost. Клиент использует шаблон Spring REST. Я начал с наивного подхода:
RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter()));
Result result = restTemplate.postForObject(url, payload, Result.class);
Когда я делаю много этих запросов, я получаю следующее исключение:
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/myservice":No buffer space available (maximum connections reached?): connect; nested exception is java.net.SocketException: No buffer space available (maximum connections reached?): connect
Это связано с тем, что соединения не закрываются и не висят в состоянии TIME_WAIT. Исключение начинается, когда эфемерные порты исчерпаны. Затем выполнение ожидает, что порты будут свободными. Я вижу максимальную производительность с длинными перерывами. Скорость, которую я получаю, - это почти то, что мне нужно, но, конечно, эти соединения TIME_WAIT не очень хороши. Протестировано как на Linux (Ubuntu 14), так и на Windows (7), аналогичные результаты в разное время из-за разных диапазонов портов.
Чтобы исправить это, я попытался использовать HttpClient с HttpClientBuilder из библиотеки Apache Http Components.
RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter()));
HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(TOTAL)
.setMaxConnPerRoute(PER_ROUTE)
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
Result result = restTemplate.postForObject(url, payload, Result.class);
С этим клиентом я не вижу никаких исключений. В настоящее время клиент использует только очень ограниченное количество эфемерных портов. Но какие бы настройки я ни использовал (TOTAL и PER_ROUTE), я не могу получить требуемую производительность.
Используя команду netstat
, я вижу, что с сервером сделано не так много соединений. Я попытался установить номера на несколько тысяч, но, похоже, клиент никогда так не использует.
Есть ли что-нибудь, что я могу сделать, чтобы улучшить производительность, не открывая слишком много соединений?
UPDATE: я попытался установить количество общих и для каждого маршрута соединений до 5000 и 2500, но он по-прежнему выглядит как клиент не создает более ста (судя по netstat -n | wc -l
). Служба REST реализована с использованием JAX-RS и работает на Jetty.
UPDATE2: теперь я настроил сервер с некоторыми настройками памяти, и я получаю действительно хорошую пропускную способность. Наивный подход все еще немного быстрее, но я думаю, что это немного накладные расходы на объединение на стороне клиента.