Статьи

Предостережения HttpURLConnection

Этот кусок кода выглядит нормально для вас?

HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url.openConnection();
try (InputStream in = url.getInputStream()) {
return streamToString(in);
}
} finally {
if (connection != null) connection.disconnect();
}

Выглядит хорошо — он открывает соединение, читает из него, закрывает входной поток, освобождает соединение, и все. Но, выполняя некоторые тесты производительности и пытаясь выяснить проблему узкого места, мы обнаружили, что  disconnect() это не так безобидно, как кажется — когда мы прекратили отключать наши соединения, было в два раза больше исходящих соединений. Вот этот Javadoc:

Указывает, что другие запросы к серверу маловероятны в ближайшем будущем. Вызов disnect () не должен подразумевать, что этот экземпляр HttpURLConnection может быть повторно использован для других запросов.

И в классе:

Вызов метода disconnect () может закрыть базовый сокет, если постоянное соединение в это время не используется.

Это все еще неясно, но дает нам намек на то, что есть нечто большее. После прочтения пары ответов stackoverflow и java.net ( 1234 ), а также  документации Android  того же класса, которая фактически отличается от реализации Oracle, оказывается, что она .disconnect() фактически закрывается (или может закрываться, в случае андроида) основной сокет.

Затем мы можем найти эту  часть документации  (она связана с javadoc, но не сразу очевидно, что это важно при вызове connect), что дает нам полную картину:

keep.alive Свойство ( по умолчанию: правда) указывает на то, что сокеты могут быть повторно использованы в последующих запросах. Это работает, оставляя соединение с сервером (который поддерживает поддержание активности) открытым, и тогда больше не нужны накладные расходы на открытие сокета. По умолчанию до 5 таких сокетов используются повторно (для каждого пункта назначения). Вы можете увеличить размер пула, установив http.maxConnections свойство. Однако после увеличения до 10, 20 и 50 видимых улучшений в количестве исходящих запросов не произошло.

Однако, когда мы перешли от  HttpURLConnection к  Apache HTTP — клиент , с объединенном диспетчера соединений, у нас было в 3 раза больше исходящих соединений в секунду. И это без тонкой настройки.

Нагрузочное тестирование, то есть бомбардировка целевого сервера как можно большим количеством запросов, звучит как ниша. Но на самом деле, если ваше приложение вызывает веб-сервис, либо в вашем стеке, либо во внешнем, как часть каждого запроса, у вас возникает та же проблема — вы сможете делать меньше запросов в секунду к целевому серверу, и, следовательно, отвечайте на меньшее количество запросов в секунду для ваших пользователей.

The advice here is: almost always prefer apache http client – it has a way better API and it seems way better performance, without the need to understand how exactly it functions underneath. But be careful of the same caveats there as well – check pool size and connection reuse. If using HttpURLConnection, do not disconnect your connections after you read their response, consider increasing the socket pool size, and be careful of related problems.