Статический потокобезопасный HttpClient на ASP.net HttpRequest

Мы создаем оболочку для HttpClient. Поскольку мы собираемся следовать рекомендациям по оптимизации производительности с https://github.com/mspnp/performance-optimization. Мы хотим избежать анти-шаблона — неправильного создания, упомянутого в этом документе. Я передал это руководство своей команде по использованию статического HttpClient. Отзывы, которые я получил, относятся к потокобезопасности. Каждый запрос имеет заголовок, содержащий утверждение пользователя. Поскольку у меня есть статический HttpClient, будет ли он потокобезопасным? Если у нас есть несколько запросов, попадающих в код (например, GET) одновременно, будет ли это условием гонки для установки заголовка? У нас есть реализация, как показано ниже.

public class HttpClientHelper{
private static readonly HttpClient _HttpClient;
static HttpClientHelper() {
        HttpClient = new HttpClient();
        HttpClient.Timeout = TimeSpan.FromMinutes(SOME_CONFIG_VALUE);
}

public async Task<HttpResponseMessage> CallHttpClientPostAsync(string requestUri, HttpContent requestBody)
{
    AddHttpRequestHeader(httpClient);
    var response = await httpClient.PostAsync(requestUri, requestBody); //Potential thread synchronization issue???
    return response;
}

public HttpResponseMessage CallHttpClientGet(string requestUri)
{
    AddHttpRequestHeader(httpClient);
    var response = httpClient.GetAsync(requestUri).Result; //Potential thread synchronization issue???
    return response;
}

private void AddHttpRequestHeader(HttpClient client)
{
    string HeaderName = "CorrelationId";
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Properties.Settings.Default.HttpClientAuthHeaderScheme, GetTokenFromClaims()); //Race condition???
    if (client.DefaultRequestHeaders.Contains(HeaderName))
        client.DefaultRequestHeaders.Remove(HeaderName);
    client.DefaultRequestHeaders.Add(HeaderName, Trace.CorrelationManager.ActivityId.ToString());
}

}


person sadhat75    schedule 21.04.2015    source источник
comment
Есть ли причина, по которой CallHttpClientGet не является асинхронным? Вызывая .Result, вы блокируете поток и провоцируете потенциальные взаимоблокировки.   -  person Todd Menier    schedule 21.04.2015


Ответы (1)


Ваша команда права, это далеко не потокобезопасно. Рассмотрим этот сценарий:

  • Поток A устанавливает для заголовка CorrelationId значение «foo».
  • Поток B устанавливает для заголовка CorrelationId значение «bar».
  • Поток A отправляет запрос, который содержит CorrelationId потока B.

Лучшим подходом для ваших методов CallXXX было бы создание нового HttpRequestMessage, установите для них заголовок и используйте HttpClient.SendAsync для сделать звонок.

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

person Todd Menier    schedule 21.04.2015
comment
Имейте также в виду, что повторное использование экземпляров HttpClient полезно только в том случае, если вы делаете несколько вызовов на один и тот же хост - у вас есть ссылка на это? - person Ohad Schneider; 04.07.2017
comment
@OhadSchneider Он основан на совете Дэрила Миллера использовать один экземпляр для каждого отдельного API, к которому вы подключаетесь. Причина в том, что преимущества производительности (отсутствие необходимости открывать новое соединение и т. д.) актуальны только для каждого хоста, как и некоторые свойства HttpClient, такие как DefatultHeaders. Однако известная теперь проблема с сокетом может изменить мой совет. маленький. Может ли Windows восстановить сокет в течение TIME_WAIT для использования с другим хостом? Я не уверен. Я разместил вопрос в этой статье. - person Todd Menier; 16.07.2017