недавно я наткнулся на это сообщение в блоге от asp.net monsters, в котором говорится о проблемах с использованием HttpClient
следующим образом:
using(var client = new HttpClient())
{
}
Согласно сообщению в блоге, если мы располагаем HttpClient
после каждого запроса, он может держать соединения TCP открытыми. Это может потенциально привести к System.Net.Sockets.SocketException
.
Правильный способ в соответствии с постом состоит в том, чтобы создать один экземпляр, так HttpClient
как это помогает уменьшить растрату сокетов.
Из поста:
Если мы будем использовать один экземпляр HttpClient, мы сможем сократить потери сокетов, используя их повторно:
namespace ConsoleApplication { public class Program { private static HttpClient Client = new HttpClient(); public static void Main(string[] args) { Console.WriteLine("Starting connections"); for(int i = 0; i<10; i++) { var result = Client.GetAsync("http://aspnetmonsters.com").Result; Console.WriteLine(result.StatusCode); } Console.WriteLine("Connections done"); Console.ReadLine(); } } }
Я всегда выбрасывал HttpClient
объект после его использования, так как чувствовал, что это лучший способ его использования. Но этот пост в блоге заставляет меня чувствовать, что я все это время делал неправильно.
Должны ли мы создать новый единый экземпляр HttpClient
для всех запросов? Есть ли подводные камни в использовании статического экземпляра?
источник
Close()
или инициируете новоеGet()
. Если вы просто избавитесь от клиента, когда закончите с ним, никто не сможет справиться с этим заключительным рукопожатием, и все ваши порты будут в состоянии TIME_WAIT, из-за этого.Ответы:
Это похоже на убедительный пост в блоге. Однако, прежде чем принять решение, я сначала запустил те же тесты, что и автор блога, но на вашем собственном коде. Я также попытался бы узнать немного больше о HttpClient и его поведении.
Этот пост гласит:
Итак, что, вероятно, происходит при совместном использовании HttpClient, это то, что соединения используются повторно, что хорошо, если вам не требуются постоянные соединения. Единственный способ узнать наверняка, имеет ли это значение для вашей ситуации, - это запустить собственные тесты производительности.
Если вы покопаетесь, вы найдете несколько других ресурсов, посвященных этой проблеме (включая статью Microsoft Best Practices), так что, вероятно, это хорошая идея для реализации в любом случае (с некоторыми мерами предосторожности).
Рекомендации
Вы используете Httpclient неправильно, и это дестабилизирует ваше программное обеспечение
Singleton HttpClient? Остерегайтесь этого серьезного поведения и способов его исправления.
Microsoft Patterns and Practices - Оптимизация производительности: неправильная реализация
Один экземпляр многоразового использования HttpClient при проверке кода
Singleton HttpClient не учитывает изменения DNS (CoreFX)
Общие рекомендации по использованию HttpClient
источник
Я опаздываю на вечеринку, но вот мое учебное путешествие по этой хитрой теме.
1. Где мы можем найти официального адвоката по повторному использованию HttpClient?
Я имею в виду, что если повторное использование HttpClient предназначено и это важно , то такой адвокат лучше задокументирован в собственной документации API, а не скрыт во множестве «Расширенных тем», «Производительных (анти) шаблонов» или других постов в блоге. , Иначе как новый ученик должен знать это, пока не стало слишком поздно?
На данный момент (май 2018 г.) первый результат поиска при поиске в Google «c # httpclient» указывает на эту справочную страницу API в MSDN , которая вообще не упоминает об этом намерении. Итак, урок 1 для новичка: всегда нажимайте ссылку «Другие версии» сразу после заголовка страницы справки MSDN, там вы, вероятно, найдете ссылки на «текущую версию». В этом случае HttpClient, он приведет вас к последнему документу, содержащему описание этого намерения .
Я подозреваю, что многие разработчики, которые были новичками в этой теме, также не нашли правильную страницу документации, поэтому эти знания не получили широкого распространения, и люди были удивлены, когда узнали об этом позже , возможно, с трудом .
2. Понятие (не так?)
using
IDisposable
Это одна немного не по теме , но все же стоит отметить, что это не совпадение , чтобы увидеть людей в тех вышеупомянутых блогах обвиняющих как
HttpClient
«SIDisposable
интерфейс делает их , как правило , использоватьusing (var client = new HttpClient()) {...}
шаблон , а затем привести к этой проблеме.Я полагаю, что это сводится к негласной (ошибочной?) Концепции: «ожидается, что IDisposable объект будет недолговечным» .
ОДНАКО, хотя это, конечно, выглядит недолгим, когда мы пишем код в этом стиле:
официальная документация по IDisposable никогда не упоминает
IDisposable
объекты должны быть кратковременными. По определению, IDisposable - это просто механизм, позволяющий вам высвобождать неуправляемые ресурсы. Ничего больше. В этом смысле вы ОЖИДАЕТЕ в конечном итоге инициировать утилизацию, но это не требует, чтобы вы делали это недолгим образом.Поэтому ваша задача - правильно выбрать, когда инициировать утилизацию, исходя из требований жизненного цикла вашего реального объекта. Ничто не мешает вам использовать IDisposable в течение длительного времени:
С этим новым пониманием, теперь, когда мы возвращаемся к этому сообщению в блоге , мы можем четко заметить, что «исправление» инициализируется
HttpClient
один раз, но никогда не удаляет его, поэтому мы можем видеть из его вывода netstat, что соединение остается в состоянии ESTABLISHED, что означает, что оно имеет НЕ был правильно закрыт. Если бы он был закрыт, его состояние было бы в TIME_WAIT. На практике нет ничего страшного в том, чтобы утратить только одно открытое соединение после завершения всей вашей программы, и постер блога все еще видит увеличение производительности после исправления; но все же, концептуально неправильно обвинять IDisposable и выбирать НЕ распоряжаться им.3. Нужно ли нам помещать HttpClient в статическое свойство или даже в качестве одиночного?
Исходя из понимания предыдущего раздела, я думаю, что ответ здесь становится ясным: «не обязательно». Это действительно зависит от того, как вы организуете свой код, если вы повторно используете HttpClient И (в идеале) утилизируете его в конце концов.
Весело, что даже пример в разделе « Замечания» текущего официального документа не делает это строго правильно. Он определяет класс «GoodController», содержащий статическое свойство HttpClient, которое не будет утилизироваться; что не подчиняется тому, что подчеркивает другой пример в разделе «Примеры» : «необходимо вызвать dispose ... чтобы приложение не теряло ресурсы».
И, наконец, синглтон не без собственных проблем.
- Цитируется из этой вдохновляющей лекции "Глобальное государство и синглтоны"
PS: SqlConnection
Этот вопрос не имеет отношения к текущим вопросам и ответам, но, вероятно, его следует знать. Схема использования SqlConnection отличается. Вам НЕ нужно повторно использовать SqlConnection , потому что он будет лучше обрабатывать свой пул соединений.
Разница обусловлена их подходом к реализации. Каждый экземпляр HttpClient использует свой собственный пул соединений (цитируется здесь ); но сам SqlConnection управляется центральным пулом соединений, в соответствии с этим .
И вам все еще нужно избавиться от SqlConnection, так же, как вы должны делать для HttpClient.
источник
Я сделал некоторые тесты увидеть улучшения производительности со статическим
HttpClient
. Я использовал приведенный ниже код для моего тестирования:Для тестирования:
Я обнаружил улучшение производительности от 40% до 60% при использовании статического
HttpClient
вместо того, чтобы утилизировать его дляHttpClient
запроса. Я поместил детали результата теста производительности в сообщение в блоге здесь .источник
Чтобы правильно закрыть соединение TCP , нам нужно завершить последовательность пакетов FIN - FIN + ACK - ACK (точно так же, как SYN - SYN + ACK - ACK, при открытии соединения TCP ). Если мы просто вызываем метод .Close () (обычно это происходит при удалении HttpClient ), и мы не ждем, пока удаленная сторона подтвердит наш запрос на закрытие (с помощью FIN + ACK), мы получим состояние TIME_WAIT на локальный порт TCP, потому что мы избавились от нашего слушателя (HttpClient), и у нас никогда не было возможности сбросить состояние порта в надлежащее закрытое состояние, как только удаленный узел отправит нам пакет FIN + ACK.
Правильный способ закрыть TCP-соединение - вызвать метод .Close () и подождать, пока событие close с другой стороны (FIN + ACK) придет на нашу сторону. Только тогда мы можем отправить наш окончательный ACK и утилизировать HttpClient.
Просто добавьте, что имеет смысл держать TCP-соединения открытыми, если вы выполняете HTTP-запросы, из-за HTTP-заголовка «Connection: Keep-Alive». Более того, вы можете попросить удаленный узел закрыть соединение вместо вас, установив HTTP-заголовок «Connection: Close». Таким образом, ваши локальные порты всегда будут правильно закрыты, вместо того, чтобы находиться в состоянии TIME_WAIT.
источник
Вот базовый клиент API, который эффективно использует HttpClient и HttpClientHandler. Когда вы создаете новый HttpClient для выполнения запроса, возникает много накладных расходов. НЕ воссоздайте HttpClient для каждого запроса. Повторно используйте HttpClient ...
Использование:
источник
Нет единственного способа использовать класс HttpClient. Ключ заключается в том, чтобы спроектировать ваше приложение так, чтобы оно имело смысл для его среды и ограничений.
HTTP - это отличный протокол, который нужно использовать, когда вам нужно предоставить общедоступные API. Он также может эффективно использоваться для облегченных внутренних служб с низкой задержкой - хотя шаблон очереди сообщений RPC часто является лучшим выбором для внутренних служб.
Существует много сложностей в правильном выполнении HTTP.
Учтите следующее:
Но, прежде всего, проверить, измерить и подтвердить. Если он не работает так, как задумано, тогда мы можем ответить на конкретные вопросы о том, как достичь ожидаемых результатов.
источник
HttpClient
реализуетIDisposable
. Поэтому вполне разумно ожидать, что это будет недолговечный объект, который умеет убирать за собой, подходящий для включения вusing
оператор каждый раз, когда он вам нужен. К сожалению, это не совсем так. Сообщение в блоге, на которое ссылается OP, ясно демонстрирует, что существуют ресурсы (в частности, соединения сокетов TCP), которые живут долго после того, какusing
оператор вышел из области видимости, иHttpClient
объект предположительно был удален.