Singleton httpclient против создания нового запроса httpclient

Я пытаюсь создать слой для webservice с помощью HttpClient в своем мобильном приложении Xamarin.Forms.

  1. без синглтонного рисунка
  2. с одноточечным рисунком

в первом подходе я создаю новый клиентский объект http в каждом новом запросе, сделанном мобильным аппликатором.

вот мой код

  public HttpClient GetConnection()
        {

            HttpClient httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(baseAddress); 
            httpClient.Timeout = System.TimeSpan.FromMilliseconds(timeout);


            return httpClient;

        }

почтовый запрос

 public async Task<TResult> PostAsync<TRequest, TResult>(String url, TRequest requestData)
        {
            HttpClient client = GetConnection();
            String responseData = null;
            if (client != null)
            {

                String serializedObject = await Task.Run(() => JsonConvert.SerializeObject(requestData, _jsonSerializerSettings));
                var jsonContent = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
                HttpResponseMessage response = await client.PostAsync(new Uri(url, UriKind.Relative), jsonContent);
                responseData = await HandleResponse(response);


                return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _jsonSerializerSettings));


            }
            else
            {

                throw new NullReferenceException("NullReferenceException @ PostAsync  httpclient is null WebRequest.cs");

            }

        }

клиент будет использовать следующий код для выполнения запроса

new LoginService(new WebRequest()).UserLogin(userRequest);

внутри класса, который реализует IWebRequest

_webRequest.PostAsync<UserRequest,bool>(Constants.USER_LOGIN, userRequest);

во втором подходе я повторно использую один и тот же объект http-клиента в каждом новом запросе здесь, мой одноэлементный класс тоже потокобезопасен.

private static readonly Lazy<HttpService> lazy =
        new Lazy<HttpService>(() => new HttpService());

        public static HttpService Instance { get { return lazy.Value; } }



        private HttpClient getConnection()
        {

            client = new HttpClient();
            client.Timeout = System.TimeSpan.FromMilliseconds(timeout);

            //client.MaxResponseContentBufferSize = 500000;
            client.BaseAddress = new Uri(baseAddress);
            return client;
        }

почтовый запрос

public Task<HttpResponseMessage> sendData(String url,String jsonData)
        {

            var jsonContent = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");

            return getConnection().PostAsync(new Uri(url, UriKind.Relative), jsonContent);
        }

клиент будет использовать следующий код для выполнения

HttpService.Instance.sendData(...)

я прошел через многие библиотеки, такие как RestSharp через Интернет, чтобы исследовать лучшее, и я обнаружил, что большинство из них создают новые объекты за запрос. поэтому я смущен, какой образец подходит лучше всего.

Ответ 1

Обновление: кажется, что использование одного статического экземпляра HttpClient не HttpClient изменения DNS, поэтому решение состоит в том, чтобы использовать HttpClientFactory. Смотрите здесь документы Microsoft об этом.

Чтобы использовать HttpClientFactory вы должны использовать внедрение зависимостей Microsoft. Это значение по умолчанию для проектов ASP.NET Core, но для других вы должны будете ссылаться на Microsoft.Extensions.Http и Microsoft.Extensions.DependencyInjection.

Затем, когда вы создаете свой сервисный контейнер, вы просто вызываете AddHttpClient():

var services = new ServiceCollection();
services.AddHttpClient()
var serviceProvider = services.BuildServiceProvider();

И затем вы можете внедрить HttpClient в свои службы, и за кулисами HttpClientFactory будет поддерживать пул объектов HttpClientHandler - поддерживая ваш DNS свежим и предотвращая проблемы с исчерпанием пула соединений.


Старый ответ:

Singleton - это правильный способ использования HttpClient. Пожалуйста, смотрите эту статью для более подробной информации.

Документы Microsoft заявляют:

HttpClient предназначен для создания экземпляра один раз и повторного использования в течение всего жизненного цикла приложения. Создание класса HttpClient для каждого запроса приведет к исчерпанию количества сокетов, доступных при больших нагрузках. Это приведет к ошибкам SocketException. Ниже приведен пример правильного использования HttpClient.

И действительно, мы нашли это в нашем приложении. У нас есть код, который потенциально может выполнять сотни запросов API в цикле foreach, и для каждой итерации мы создавали HttpClient завернутый в using. Вскоре мы начали получать ошибки "красной сельди" от нашего MongoClient, что истекло время попытки подключения к базе данных. Прочитав связанную статью, мы обнаружили, что даже после удаления HttpClient, и поняли, что мы HttpClient доступные сокеты.

Единственное, на что следует обратить внимание, - это то, что DefaultRequestHeaders и BaseAddress будут применяться везде, где используется HttpClient. Как единое целое, это потенциально во всем приложении. Вы по-прежнему можете создавать несколько экземпляров HttpClient в своем приложении, но HttpClient в HttpClient что каждый раз, когда вы это делаете, они создают новый пул соединений и, как таковые, должны создаваться экономно.

Как указывает hvaughan3, вы также не можете изменить экземпляр HttpMessageHandler используемый HttpClient, поэтому, если это важно для вас, вам потребуется использовать отдельный экземпляр с этим обработчиком.

Ответ 2

Хотя HttpClient предполагается использовать повторно, это не обязательно означает, что мы должны использовать синглтон для организации нашего кода. Пожалуйста, обратитесь к моему ответу здесь. Также цитируется ниже.


Я опаздываю на вечеринку, но вот мое учебное путешествие по этой хитрой теме.

1. Где мы можем найти официального адвоката по повторному использованию HttpClient?

Я имею в виду, что если повторное использование HttpClient предназначено и это важно, такой адвокат лучше задокументирован в собственной документации API, а не скрыт во множестве "Расширенных тем", "Производительных (анти) шаблонов" или других постов в блоге., Иначе как новый ученик должен знать это, пока не стало слишком поздно?

На данный момент (май 2018 г.) первый результат поиска при поиске в Google "С# httpclient" указывает на эту справочную страницу API в MSDN, которая вообще не упоминает об этом намерении. Итак, урок 1 для новичка: всегда нажимайте ссылку "Другие версии" сразу после заголовка страницы справки MSDN, там вы, вероятно, найдете ссылки на "текущую версию". В этом случае HttpClient, он приведет вас к последнему документу, содержащему описание этого намерения.

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

2. (неправильная?) Концепция using IDisposable

Это немного не по теме, но все же стоит отметить, что не случайно люди в этих вышеупомянутых публикациях в блогах обвиняют то, HttpClient интерфейс HttpClient IDisposable заставляет их склоняться к использованию using (var client = new HttpClient()) {...} шаблон, а затем привести к проблеме.

Я полагаю, что это сводится к негласной (ошибочной?) Концепции: "ожидается, что IDisposable объект будет недолговечным".

ОДНАКО, хотя это, конечно, выглядит недолгим, когда мы пишем код в этом стиле:

using (var foo = new SomeDisposableObject())
{
    ...
}

Официальная документация по IDisposable никогда не упоминает, что IDisposable объекты должны быть недолговечными. По определению, IDisposable - это просто механизм, позволяющий вам высвобождать неуправляемые ресурсы. Ничего более. В этом смысле вы ОЖИДАЕТЕ в конечном итоге инициировать утилизацию, но это не требует, чтобы вы делали это недолгим образом.

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

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

С этим новым пониманием, теперь, когда мы возвращаемся к этому сообщению в блоге, мы можем четко заметить, что "исправление" инициализирует HttpClient один раз, но никогда не HttpClient его, поэтому мы можем видеть из его вывода netstat, что соединение остается в состоянии ESTABLISHED, что означает его НЕ был правильно закрыт. Если бы он был закрыт, его состояние было бы в TIME_WAIT. На практике нет ничего страшного в том, чтобы утратить только одно открытое соединение после завершения всей вашей программы, и постер блога все еще видит увеличение производительности после исправления; но все же, концептуально неправильно обвинять IDisposable и выбирать НЕ распоряжаться им.

3. Нужно ли нам помещать HttpClient в статическое свойство или даже в качестве одиночного?

Исходя из понимания предыдущего раздела, я думаю, что ответ здесь становится ясным: "не обязательно". Это действительно зависит от того, как вы организуете свой код, если вы повторно используете HttpClient И (в идеале) утилизируете его в конце концов.

Весело, что даже пример в разделе " Замечания" текущего официального документа не делает это строго правильно. Он определяет класс "GoodController", содержащий статическое свойство HttpClient, которое не будет утилизироваться; что не подчиняется тому, что подчеркивает другой пример в разделе "Примеры": "необходимо вызвать dispose... чтобы приложение не теряло ресурсы".

И, наконец, синглтон не без собственных проблем.

"Сколько людей считают глобальную переменную хорошей идеей? Никто.

Сколько людей считают синглтон хорошей идеей? Немного.

Что дает? Синглтоны - это просто набор глобальных переменных ".

- Цитируется из этой вдохновляющей лекции "Глобальное государство и синглтоны"

PS: SqlConnection

Этот вопрос не имеет отношения к текущим вопросам и ответам, но, вероятно, его следует знать. Шаблон использования SqlConnection отличается. Вам НЕ нужно повторно использовать SqlConnection, потому что он будет лучше обрабатывать свой пул соединений.

Разница обусловлена их подходом к реализации. Каждый экземпляр HttpClient использует свой собственный пул соединений (цитируется здесь); но сам SqlConnection управляется центральным пулом соединений, в соответствии с этим.

И вам все еще нужно избавиться от SqlConnection, так же, как вы должны делать для HttpClient.

Ответ 3

Как уже упоминалось, в основном, HttpClient должен использоваться как синглтон, но есть одно исключение - вы не должны использовать HttpClient как синглтон, когда используете технику HTTP long polling, потому что вы будете блокировать выполнение других запросов.

Для длинных запросов на опрос вы должны создать отдельный HttpClient.