7

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

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()); 
} 

}

+1

Любая причина, по которой 'CallHttpClientGet' не является асинхронным? Вызывая «.Result», вы блокируете поток и приглашаете потенциальные блокировки. –

ответ

10

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

  • Thread A устанавливает CorrelationId заголовок в "foo".
  • Thread B устанавливает CorrelationId заголовок в "bar".
  • Thread A отправляет запрос, который содержит резьбу B CorrelationId.

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

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

+0

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

+2

@OhadSchneider. Он основан на [совете Дэрила Миллера] (https://stackoverflow.com/a/22561368/62600), чтобы использовать один экземпляр «для каждого отдельного API, к которому вы подключаетесь». Причина в том, что преимущества производительности (не имея необходимости открывать новое соединение и т. Д.) Имеют значение только для каждого хоста, а также некоторые свойства HttpClient, такие как DefatultHeaders. Однако теперь известная проблема сокета (https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/) может немного изменить мой совет. Может ли Windows восстановить сокет в TIME_WAIT для использования с другим хостом? Я не уверен. Я поставил вопрос на эту статью. –