Adi_Mag Ответов: 2

Создайте экземпляр httpclient для каждого webrequest или одного экземпляра.


Всем Привет,

Я прочитал так много статей о HttpClient. В некоторых статьях упоминается создание одного экземпляра httpclient и повторное использование его во всем приложении. Но в некоторых статьях упоминается создание нового экземпляра httpclient для каждого webrequest.

**// New instance per web request. It disposes the httpClient after use.**
using(var httpClient = new HttpClient())
{
   //  Get/ Post/ Put/ Delete
}

Не могли бы вы сообщить мне, какой подход лучше?

Я создал небольшой POC. Тест 1: один экземпляр httpclient.

private static readonly HttpClient _httpClient = new HttpClient();
// Note : Application Hosted in Local IIS
private const string ServerGetUrl = "http://localhost:80/api/service/getdata";
private static void TestPingWithSingleInstance()
    {

        Parallel.For(0, 5,
            i =>
            {
                    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    var response = _httpClient.GetAsync(ServerGetUrl).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("==============="); 
            });
}

Когда я запустил команду netstat. Он отображает 5 открытых портов.

TCP 127.0.0.1:46336 DESKTOP-OEOGI0L:HTTP установлен
TCP 127.0.0.1:46337 DESKTOP-OEOGI0L:HTTP установлен
TCP 127.0.0.1:46338 DESKTOP-OEOGI0L:HTTP установлен
TCP 127.0.0.1:46339 DESKTOP-OEOGI0L:HTTP установлен
TCP 127.0.0.1:46340 DESKTOP-OEOGI0L:HTTP установлен
Тест 2: создание экземпляра для каждого веб-запроса.

private static void TestPingWithDisposableInstance()
{
using (var httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    var response = httpClient.GetAsync(ServerGetUrl).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("===============");
                }

}

Когда я запустил команду netstat. Он отображает 5 открытых портов.

TCP 127.0.0.1:46233 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46234 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46235 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46236 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46237 DESKTOP-OEOGI0L:http TIME_WAIT


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

Примечание: Я использую .NET 4.5.1 framework.

Заранее спасибо.

Что я уже пробовал:

Я создал выше POC. Одинаковое количество портов открыто для обоих подходов.

Thomas Daniels

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

2 Ответов

Рейтинг:
6

Adi_Mag

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

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

Но в специфических для пользователя сценариях. Нам нужно установить заголовки в соответствии с пользователями. Таким образом, возникает вопрос, как справиться с такого рода сценариями ?
Решение :

Parallel.For(0, 10,
                i =>
                {
                    var guidData = Guid.NewGuid().ToString();
                    _httpClient.DefaultRequestHeaders.Clear();
                    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                        
var response = _httpClient.CustomGetAsync("http://localhost:2967/api/service/GetData", x => x.Headers.Add("Guid", guidData)).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("===============");
                });<pre lang="c#">



Создать метод расширения для манипулирования заголовком :
public static Task<HttpResponseMessage> CustomGetAsync(this HttpClient httpClient, string url, Action<HttpRequestMessage> manipulateData)
        {
            var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);

            manipulateData(httpRequestMessage);

            return httpClient.SendAsync(httpRequestMessage);
        }


Рейтинг:
15

Thomas Daniels

Оба способа будут работать. HttpClient можно использовать повторно.

Я рекомендую использовать один экземпляр HttpClient. Имея один экземпляр, вы можете извлечь выгоду из Свойство DefaultRequestHeaders[^] и Собственность базовому адресу [^Еще более важно то, что простое использование одного экземпляра HttpClient повысит производительность и стабильность вашего приложения. Этот пост в блоге подробно описывает это: Вы неправильно используете HttpClient и это дестабилизирует ваше программное обеспечение | ASP.NET монстры[^]


Adi_Mag

Мой поток приложений-клиент=== & gt;webapi== = & gt;сервер аутентификации. Мы получаем доступ из запроса клиента. Затем мы подключаемся к серверу аутентификации для проверки. Для этой проверки мы используем httpclient. Нам нужно установить заголовок доступа httpclient с токеном доступа. Если я использую один и тот же экземпляр HttpClient, то возникает условие гонки.

Простой СВ :

Параллель. Для(0, 10,
i =>
{
ВАР guidData = идентификатор GUID.Метод newguid().Метод toString();
_httpClient.DefaultRequestHeaders.Четкий();
_httpClient.DefaultRequestHeaders.Accept. Add(new MediaTypeWithQualityHeaderValue ("application/json"));
_httpClient.DefaultRequestHeaders. Add ("Guid", guidData);
ответ ВАР = _httpClient.С getasync("http://localhost:2967/api/service/GetData").GetAwaiter (). GetResul();
var data = ответ.Содержание.ReadAsStringAsync ().Помощью getawaiter().Метод getresult();
Приставка.WriteLine(данные);
Приставка.метод WriteLine("===============");
});

Ошибка: получение ошибки, guid уже добавлен.

Thomas Daniels

Не думаю, что понимаю. Ваш поток приложений кажется серией шагов, но вы используете параллель в своем коде? Если это параллельное выполнение вызывает состояние гонки, то единственный HttpClient здесь не виноват.

Adi_Mag

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

Thomas Daniels

Ага, я вижу, в чем дело. Вы устанавливаете DefaultRequestHeaders каждый раз в цикле. Это неправильно: это по умолчанию для причины. Вы должны установить его вне цикла. Это просто не будет работать для заголовка "Guid", потому что он меняется каждый раз в цикле, поэтому я бы просто рекомендовал не использовать для этого заголовок. Если вы используете GET, вы можете передать его в строке запроса. Если вы используете POST (что я бы действительно рекомендовал в данном случае вместо GET), то передайте guid в качестве данных POST. Здесь нет причин использовать заголовки. Это также потребует некоторых изменений на вашей стороне сервера.

Adi_Mag

Большое спасибо.

Thomas Daniels

Пожалуйста.

Adi_Mag

Еще одна точка :
Получить конечную точку отдыха : http://www.customerservices.com/Customers/{id}? access_token=12345
Правильный ли это подход?
Мой предыдущий подход состоял в том, чтобы отправить этот access_token в DefaultRequestHeader. Но defaultRequsetHeader вызывает проблему.

Thomas Daniels

Он работает для запросов GET. Однако для токенов доступа я думаю, что буду использовать POST-запросы, используя HttpClient.PostAsync: https://msdn.microsoft.com/en-us/library/hh138242 (v=vs.118). aspx и поместите access_token в данные POST.

Adi_Mag

Сервис-это защищенный сервис. Чтобы получить доступ к get_endpoint. Нам нужно передать access_token. Мы не можем нарушать принципы rest, делая запрос get как post. Есть какие-нибудь мысли по этому поводу ?