2017-02-22 16 views
4

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

Do HttpClient and HttpClientHandler have to be disposed?

What is the overhead of creating a new HttpClient per call in a WebAPI client?

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

Я даже продолжал делать это синглтон, как показано ниже, поэтому есть только один экземпляр HttpClient, используемый во всем приложении. (Статья четвертой формы версии Джона Скита)

http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class MyHttpClient 
{  
    private static readonly volatile HttpClient _myHttpClient = new HttpClient(); 

    static MyHttpClient() {} 
    private MyHttpClient(){ } 

    public static HttpClient MyHttpClientObj 
    { 
     get 
     {    
      return _myHttpClient; 
     } 
    } 
} 

И ниже приведен пример того, как это привыкает

public IEnumerable<string> GetSomeData(string url, FormUrlEncodedContent bodyParameters) 
    { 
     try 
     { 
      //is it possible to configure timeout here instead, such that every request will have its one timeout duration? 
      var response = MyHttpClient.MyHttpClientObj.PostAsync(url, bodyParameters); 

      var result = response.Result; 

      if (!result.IsSuccessStatusCode) 
      { 
      //log and return null 
      } 

      var data = JsonConvert.DeserializeObject<List<string>>(result.Content.ReadAsStringAsync().Result); 

      return data; 

     } 
     catch (Exception ex) 
     { 
      //logging exceptions 
     } 
    } 

При выполнении запросов через HttpClient, я удостоверился, чтобы использовать только перечисленные ниже безопасные методы, но при десериализации ответа используется result.Content.ReadAsStringAsync().Result. Это связано с тем, что вызовы более высокого уровня еще не поддерживают асинхронные ответы.

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.110).aspx#Anchor_5

Однако, я до сих пор есть несколько вопросов.

  1. Является ли этот подход надежным и стабильным потоком, чтобы не вызвать утечек памяти?
  2. Как настроить таймаут для каждого запроса?
  3. Нужно ли указывать 'Connection: keep-alive' в DefaultHeaders?
  4. Как добавить пользовательский заголовок/изменить заголовок по умолчанию для каждого запроса?
  5. И, наконец, Существуют ли какие-либо известные проблемы с производительностью/недостатки в использовании HttpClient таким образом?

ответ

1
  1. Да, этот подход является поточно-безопасным, как вы называете потокобезопасные методы и не вам никакой логики синхронизации, так что клиентские потоки просто независимы друг от друга.
  2. Вы можете использовать перегрузку с CancellationTokenSource, с вызовом метода CancelAfter, этот подход рекомендуется MSDN.
  3. Нет, но если для вашего соединения требуется некоторое взаимодействие между клиентом и сервером, это очень рекомендуемый подход для HTTP/1.1, он уменьшает накладные расходы для воссоздания соединения сокета и некоторых рукопожатий между участвующими сторонами.
  4. Вы можете использовать свойство Headers класса FormUrlEncodedContent, просто добавьте заголовок, который вам нужен.
  5. Огромный недостаток вашего решения - .Result вызов, поскольку он блокирует текущую нить. Вы можете попытаться реорганизовать свой подход с использованием TaskCompletionSource, чтобы вы могли использовать методы async внутри.Это даст вам возможность для потоков делать что-то еще, а не ждать результата.
1

Самая большая проблема с производительностью, с которой вы столкнулись с использованием HttpClient в высококонкурентной среде, - количество одновременных подключений к любому заданному URL ограничено по умолчанию. Вы можете увеличить это для всех конечных точек, используя ServicePointManager.DefaultConnectionLimit или получить конкретный ServicePoint с использованием ServicePointManager.FindServicePoint и установить ServicePoint.ConnectionLimit

1

Это потокобезопасный и рекомендуется.

В зависимости от вашего использования, самая большая вещь может поднять лимит подключения до нужного уровня параллелизма:

ServicePointManager.DefaultConnectionLimit = 16; 

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

Я также рекомендую использовать конвейерную для повышения производительности:

new HttpClient(new WebRequestHandler() { AllowPipelining = true }); 
Смежные вопросы