Чи повинні HttpClient та HttpClientHandler розміщуватися між запитами?


334

System.Net.Http.HttpClient і System.Net.Http.HttpClientHandler в .NET Framework 4.5 реалізують IDisposable (через System.Net.Http.HttpMessageInvoker ).

Документація usingзаяви:

Як правило, коли ви використовуєте об'єкт, що не використовується, ви повинні оголосити його та використати в операторі, що використовує.

У цій відповіді використовується така модель:

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

Але найбільш видимі приклади від Microsoft не називають Dispose()явно або неявно. Наприклад:

У оголошення «S коментарі, хто - то запитав співробітник Microsoft:

Перевіривши ваші зразки, я побачив, що ви не здійснили розпорядження в екземплярі HttpClient. Я використовував усі екземпляри HttpClient із використанням заяви на своєму додатку, і я вважав, що це правильний шлях, оскільки HttpClient реалізує інтерфейс IDisposable. Я на вірному шляху?

Його відповідь:

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

До речі, той самий HttpClient ви можете повторно використовувати стільки разів, скільки вам подобається, як правило, ви не будете створювати / розпоряджатися ними постійно.

Другий абзац зайвий у цьому питанні, яке не стосується того, скільки разів ви можете використовувати екземпляр HttpClient, а про те, чи потрібно його розпоряджатися після того, як він вам більше не потрібен.

(Оновлення: адже другий абзац є ключем до відповіді, як надано нижче @DPeden.)

Тому мої запитання:

  1. Чи потрібно, враховуючи поточну реалізацію (.NET Framework 4.5), викликати Dispose () в екземплярах HttpClient та HttpClientHandler? Пояснення: під "необхідним" я маю на увазі, якщо є якісь негативні наслідки для нерозпорядження, такі як витоки ресурсів або ризик корупції даних.

  2. Якщо це не потрібно, чи це все-таки "хороша практика", оскільки вони реалізують IDisposable?

  3. Якщо це необхідно (або рекомендовано), чи вказаний вище код безпечно реалізує його (для .NET Framework 4.5)?

  4. Якщо для цих класів не потрібно викликати Dispose (), чому вони були реалізовані як IDisposable?

  5. Якщо вони вимагають, або якщо це рекомендована практика, чи є приклади Microsoft оманливими чи небезпечними?


2
@Damien_The_Unbeliever, дякую за відгук. Чи є у вас якісь пропозиції щодо того, як я міг би уточнити це питання? Мені хочеться знати, чи це може призвести до проблем, які, як правило, пов'язані з нерозпорядженням ресурсів, таких як витоки ресурсів та корупція даних.
Фернандо Коррея

9
@Damien_The_Unbeliever: Неправда. Зокрема, автори потоку повинні розміщуватись, щоб мати правильну поведінку.
Стівен Клірі

1
@StephenCleary - про які аспекти ти думаєш? Звичайно, ви можете зателефонувати Flushодин раз після кожного запису, і крім незручностей, якщо він продовжує тримати основні ресурси довше, ніж потрібно, що не відбудеться, що потрібно для "правильної поведінки"?
Damien_The_Unbeliever

1
Це зрозуміло неправильно: "Як правило, коли ви використовуєте об'єкт, що не використовується, ви повинні оголосити його і використати в операторі". Я б читав документацію про клас, що реалізує IDisposable, перш ніж вирішити, чи слід використовувати для нього використання. Оскільки автору бібліотек, де я реалізую IDisposable becuase, потрібно випускати безрегламентовані ресурси, я б жахнувся, якби споживачі створювали розпорядження кожного разу замість повторного використання наявного. Тобто не кажучи про те, що зрештою не розпоряджатися екземплярами ..
markmnl

1
Я подав PR в Microsoft, щоб оновити свої документи: github.com/dotnet/docs/pull/2470
markmnl

Відповіді:


259

Загальний консенсус полягає в тому, що вам не слід (не слід) розпоряджатися HttpClient.

Про це заявили багато людей, які тісно залучені до того, як це працює.

Дивіться допис у блозі Даррела Міллера та пов’язаний з цим повідомленням SO: Сканування HttpClient призводить до витоку пам'яті для довідки.

Я також наполегливо пропоную прочитати розділ HttpClient з Проектування еволюціонуючих веб-API з ASP.NET для контексту щодо того, що відбувається під кришкою, зокрема розділ "Життєвий цикл", цитований тут:

Незважаючи на те, що HttpClient опосередковано реалізує інтерфейс IDisposable, звичайне використання HttpClient не полягає у тому, щоб розпоряджатися ним після кожного запиту. Об'єкт HttpClient призначений для проживання стільки часу, скільки вашій програмі потрібно робити HTTP-запити. Наявність об'єкта в декількох запитах дає місце для встановлення DefaultRequestHeaders і не дозволяє вам повторно вказувати такі речі, як CredentialCache та CookieContainer в кожному запиті, як це було необхідно з HttpWebRequest.

Або навіть відкрити DotPeek.


64
Щоб уточнити свою відповідь, чи було б правильним сказати, що "вам не потрібно розпоряджатися HttpClient, якщо ви тримаєтесь на ІНСТАНЦІЮ, ЩО ВИКОРИСТОВУЄТЬСЯ" Наприклад, якщо метод викликається повторно і створює новий екземпляр HttpClient (навіть якщо це не рекомендований зразок у більшості випадків), чи все-таки правильно було б сказати, що цей метод не повинен розпоряджатися екземпляром (це не буде повторно використане)? Це може призвести до тисяч нерозкритих випадків. Іншими словами, ви повинні спробувати і повторно використати екземпляри, але якщо ви не повторно використовуєте їх, ви краще розпоряджайтесь ними (звільнити з'єднання)?
Фернандо Коррея

8
Я вважаю, що це залежить, від цього насправді страшно, але правильна відповідь. Якби мені довелося покластись на загальну пораду, яка працювала в більшості (я ніколи не кажу всіх) випадків, я б запропонував вам використовувати контейнер IoC і зареєструвати екземпляр HttpClient як синглтон. Термін експлуатації екземпляра буде потім віднесений до терміну експлуатації контейнера. Це може бути визначено на рівні програми або, можливо, за запитом у веб-додатку.
Девід Педен

25
@FernandoCorreia Так. Якщо з якоїсь причини ви багаторазово створюєте та знищуєте екземпляри HttpClient, то так, вам слід розпоряджатися цим. Я не пропоную ігнорувати інтерфейс IDisposable, просто намагаюся заохотити людей повторно використовувати екземпляри.
Даррел Міллер

20
Просто для того, щоб додати подальшу довіру до цієї відповіді, я сьогодні поговорив з командою HttpClient, і вони підтвердили, що HttpClient не розроблений для використання на запит. Екземпляр HttpClient повинен залишатися живим, поки клієнтська програма продовжує взаємодіяти з певним хостом.
Даррел Міллер

19
@DavidPeden Реєстрація HttpClient як сингл звучить для мене небезпечно, тому що він змінюється. Наприклад, чи не всі, хто припускає Timeoutмайно, тупотіли один одного?
Джон-Ерік

47

Поточні відповіді трохи заплутані та оманливі, і вони не мають важливих наслідків DNS. Я спробую підсумувати, де все чітко стоїть.

  1. Взагалі, більшість IDisposableоб'єктів в ідеалі повинні бути розміщені, коли ви закінчите з ними , особливо ті, що володіють ресурсами Named / shared OS . HttpClientне є винятком, оскільки, як зазначає Даррел Міллер , він виділяє маркери скасування, а органи запиту / відповіді можуть бути некерованими потоками.
  2. Однак найкраща практика HttpClient говорить про те, що ви повинні створити один екземпляр і максимально повторно використовувати його (використовуючи безпечні для потоків учасники у багатопотокових сценаріях). Тому в більшості сценаріїв ви ніколи не розпоряджаєтесь нею просто тому, що будете мати потребу в ньому постійно .
  3. Проблема з повторним використанням того ж HttpClient "назавжди" полягає в тому, що базове HTTP-з'єднання може залишатися відкритим проти спочатку вирішеного DNS IP-адреси, незалежно від змін у DNS . Це може бути проблемою в таких сценаріях, як синє / зелене розгортання та відхід на основі DNS . Існують різні підходи до вирішення цієї проблеми, найнадійніший - сервер, який надсилає Connection:closeзаголовок після зміни DNS. Інша можливість передбачає переробку HttpClientна клієнтській стороні, періодично або через якийсь механізм, який дізнається про зміну DNS. Дивіться https://github.com/dotnet/corefx/isissue/11224 для отримання додаткової інформації (я пропоную прочитати її уважно, перш ніж сліпо використовувати код, запропонований у пов’язаній публікації блогу).

Я розпоряджаюся цим постійно, оскільки не можу переключити проксі на екземпляр;)
ed22

Якщо вам потрібно з будь-якої причини розпоряджатись HttpClient, вам слід тримати статичний екземпляр HttpMessageHandler навколо, як розміщувати, що це насправді є причиною проблем, пов’язаних із розпорядженням HttpClient. HttpClient має перевантаження конструктора, що дозволяє вказати, що наданий обробник не повинен бути утилізований, і в цьому випадку ви можете повторно використовувати HttpMessageHandler з іншими екземплярами HttpClient.
Том Лінт

Ви повинні триматися за свій HttpClient, але ви можете використовувати щось на зразок System.Net.ServicePointManager.DnsRefreshTimeout = 3000; Це корисно, наприклад, якщо ви перебуваєте на мобільному пристрої, який у будь-який час може перемикатися між wifi та 4G.
Йохан Францен

18

На моє розуміння, дзвінок Dispose()необхідний лише тоді, коли він блокує потрібні вам ресурси пізніше (як-от певне з'єднання). Завжди рекомендується звільнити ресурси, якими ви більше не користуєтесь, навіть якщо вони вам більше не потрібні, просто тому, що зазвичай ви не повинні триматися на ресурсах, якими ви не користуєтесь (призначено каламбур).

Приклад Microsoft не обов'язково є невірним. Усі використані ресурси будуть випущені, коли програма завершиться. І у випадку з цим прикладом, це відбувається майже одразу після того, як HttpClientбуде зроблено використання. У подібних випадках явне виклик Dispose()дещо зайве.

Але загалом, коли клас реалізується IDisposable, розуміння полягає в тому, що ви повинні Dispose()його екземпляри, як тільки ви повністю готові і вмієте. Я вважаю, що це особливо справедливо у випадках, HttpClientколи в них немає чіткого документального підтвердження того, чи підтримуються / відкриваються ресурси або з'єднання. У випадку, коли з'єднання буде повторно використане [незабаром], ви захочете відмовитисяDipose() від нього - у цьому випадку ви не «повністю готові».

Дивіться також: IDisposable.Dispose Method and When to call Dispose


7
Це як якщо хтось приносить банан у ваш будинок, їсть його і стоїть їх разом з шкіркою. Що їм робити з шкіркою? ... Якщо вони з ними виходять із дверей, відпустіть їх. Якщо вони стирчать навколо, змусьте їх кидати у сміття, щоб він не смердив місця.
svidgen

просто щоб уточнити цю відповідь, ви говорите: "Не потрібно розпоряджатися, якщо програма закінчиться відразу після її використання"? І що вам слід розпоряджатися, якщо очікується, що програма ще деякий час буде робити інші справи?
Фернандо Коррея

@FernandoCorreia Так, якщо я щось не забуду, я думаю, що це безпечний принцип. Подумайте хоча б у кожному випадку. Наприклад, якщо ви працюєте з підключенням, ви не хочете Dispose()його передчасно, і вам доведеться знову підключитися через кілька секунд, якщо існуюче з'єднання може бути використане повторно. Крім того, ви не хочете зайвого Dispose()зображень чи інших структур, які, можливо, вам доведеться перебудувати за хвилину-дві.
svidgen

Я розумію. Але в конкретному випадку HttpClient і HttpClientHandler, про який йдеться у цьому питанні, чи тримають вони відкритий ресурс, такий як HTTP-з'єднання? Якщо це відбувається, мені, можливо, доведеться переосмислити свою схему використання їх.
Фернандо Коррея

1
@DPeden Ваша відповідь зовсім не суперечить моїй. Зауважте, я сказав, ви повинні розпоряджатися () його екземплярами, як тільки ви повністю готові та зможете . Якщо ви плануєте використовувати екземпляр ще раз, ви не готові .
svidgen

9

Dispose () викликає код нижче, який закриває з'єднання, відкриті екземпляром HttpClient. Код створено декомпіляцією за допомогою dotPeek.

HttpClientHandler.cs - розпоряджайтеся

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Якщо ви не зателефонували у розпорядження, тоді ServicePointManager.MaxServicePointIdleTime, який працює за допомогою таймера, закриє з'єднання http. За замовчуванням - 100 секунд.

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);

private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
  ServicePoint servicePoint = (ServicePoint) context;
  if (Logging.On)
    Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
  lock (ServicePointManager.s_ServicePointTable)
    ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
  servicePoint.ReleaseAllConnectionGroups();
}

Якщо ви не встановили час простою на безкінечність, то видається безпечним не виклик розпорядження, а дозволити таймеру простою підключення запустити та закрити з'єднання для вас, хоча вам було б краще зателефонувати розпоряджатися у використовуваному операторі, якщо ви знаєте, що ви закінчили з екземпляром HttpClient і швидше звільніть ресурси.


1
Ви також можете побачити код на github. github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/…
TamusJRoyce

8

Коротка відповідь: Ні, твердження у прийнятій на даний момент відповідь НЕ точне : "Загальна консенсус полягає в тому, що вам не потрібно (не слід) розпоряджатися HttpClient".

Довга відповідь : БОЛЬСЯ наступні твердження є істинними та досяжними одночасно:

  1. "HttpClient призначений для одноразової реєстрації та повторного використання протягом життя програми", цитується з офіційної документації .
  2. An IDisposableОб'єкт повинен / рекомендується бути утилізовані.

І вони НЕ НЕОБХІДНО конфліктують між собою. Це лише питання про те, як ви впорядкуєте свій код для повторного використанняHttpClient І все-таки розпорядження ним належним чином.

Ще довша відповідь, цитується з моєї іншої відповіді :

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

Я вважаю, що це зводиться до невисловленої (неправильної?) Концепції: "Очікується, що об'єкт, що не використовує ID, буде короткочасним". .

ЗАРАЗ, хоча це, звичайно, виглядає як недовговічна річ, коли ми пишемо код у такому стилі:

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

в офіційній документації на 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один раз, але ніколи не розпоряджається цим, тому ми можемо бачити з його результатів netstat, що з'єднання залишається у встановленому стані, що означає, що воно має НЕ було належним чином закрито. Якби він був закритий, його стан був би замість TIME_WAIT. На практиці не дуже важливо протікати лише одне підключення після закінчення всієї вашої програми, а в блозі-плакаті все-таки спостерігається підвищення продуктивності після виправлення; але все-таки, концептуально неправильно звинувачувати IDisposable і вирішити НЕ розпоряджатися ним.


дякую за це пояснення. Це явно проливає трохи світла на консенсус. З вашої думки, коли ви вважаєте, що правильно дзвонити HttpClient.Dispose?.
Jeson Martajaya

@JesonMartajaya, розпоряджайтесь нею, коли вашій програмі більше не потрібно використовувати екземпляр httpClient. Ви можете подумати, що така пропозиція звучить невиразно, але насправді вона може ідеально узгоджуватись із життєвим циклом вашої HttpClient clientзмінної, що є програмою-101, що ви, мабуть, вже робите. Можливо, ви все ще можете користуватися using (...) {...}. Наприклад, дивіться зразок Hello World всередині моєї відповіді.
RayLuo

7

Оскільки, схоже, тут ніхто не згадував, новий найкращий спосіб управління HttpClient і HttpClientHandler в. Net Core 2.1 використовує HttpClientFactory .

Він вирішує більшість вищезазначених питань та прибутків чистим та простим у використанні способом. З чудової публікації в блозі Стіва Гордона :

Додайте такі пакети до свого проекту .Net Core (2.1.1 або новішої версії):

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

Додайте це до Startup.cs:

services.AddHttpClient();

Вводити та використовувати:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

Вивчіть серію публікацій у блозі Стіва, щоб отримати ще багато функцій.


4

У моєму випадку я створював HttpClient всередині методу, який фактично робив виклик служби. Щось на зразок:

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

У ролі працівника Azure після багаторазового виклику цього методу (без розпорядження HttpClient) він, зрештою, не вдасться SocketException(спроба підключення не вдалася).

Я зробив HttpClient змінною екземпляра (розміщуючи його на рівні класу), і проблема пішла. Тому я б сказав, так, розпоряджайтесь HttpClient, вважаючи його безпечним (у вас немає видатних викликів асинхронізації) для цього.


Дякуємо за відгук. Це дещо складне питання. Рекомендую прочитати статті, пов'язані у відповіді DPeden. Коротше кажучи, екземпляр HttpClient слід повторно використовувати протягом усього життєвого циклу програми. Якщо ви створюєте нові екземпляри неодноразово, можливо, вам доведеться їх утилізувати.
Фернандо Коррея

6
"екземпляр HttpClient повинен бути повторно використаний протягом усього життєвого циклу програми", це просто хороша ідея з великою кількістю програм. Я думаю про веб-додатки, які використовують HttpClient. HttpClient утримує стан (наприклад, заголовки запитів, які він буде використовувати), тому один потік веб-запиту може легко топтати те, що робить інший. У великих веб-додатках я також бачив HttpClient як головну проблему з підключенням. Коли ви сумніваєтесь, я кажу розпорядження.
bytedev

@nashwan ви не можете очистити заголовки та додати нові перед кожним запитом?
Mandeep Janjua

Microsoft також рекомендує повторно використовувати екземпляри HttpClient
docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…

@MandeepJanjua цей приклад представляється клієнтом як консольний додаток. Я мав на увазі, що веб-додаток є клієнтом.
bytedev

3

За типового використання (відповіді <2 Гб) не потрібно розпоряджатися повідомленнями HttpResponseMessages.

Типи повернення методів HttpClient повинні бути розміщені, якщо вміст потоку не повністю прочитаний. Інакше CLR не може знати, що ці потоки можуть бути закриті, поки вони не збираються.

  • Якщо ви читаєте дані в байт [] (наприклад, GetByteArrayAsync) або рядок, всі дані читаються, тому немає необхідності розпоряджатися.
  • Інші перевантаження за замовчуванням будуть читати Потік до 2 Гб (HttpCompletionOption - ResponseContentRead, HttpClient.MaxResponseContentBufferSize за замовчуванням - 2 ГБ)

Якщо ви встановите HttpCompletionOption на ResponseHeadersRead або відповідь перевищує 2 Гб, слід очистити. Це можна зробити, зателефонувавши Dispose на HttpResponseMessage або зателефонувавши Dispose / Close on the Stream, отриманий із вмісту HttpResonseMessage, або повністю прочитавши вміст.

Від того, чи будете ви закликати розпоряджатися на HttpClient, залежить від того, чи хочете ви скасувати очікувані запити чи ні.


2

Якщо ви хочете розпоряджатися HttpClient, ви можете, якщо встановити його як пул ресурсів. І наприкінці своєї заявки ви розпоряджаєтесь своїм ресурсом.

Код:

// Notice that IDisposable is not implemented here!
public interface HttpClientHandle
{
    HttpRequestHeaders DefaultRequestHeaders { get; }
    Uri BaseAddress { get; set; }
    // ...
    // All the other methods from peeking at HttpClient
}

public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
{
    public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool;
    public static HashSet<Uri> _uris;

    static HttpClientHander()
    {
        _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>();
        _uris = new HashSet<Uri>();
        SetupGlobalPoolFinalizer();
    }

    private DateTime _delayFinalization = DateTime.MinValue;
    private bool _isDisposed = false;

    public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
    {
        HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
        _uris.Add(baseUrl);
        httpClient._delayFinalization = DateTime.MinValue;
        httpClient.BaseAddress = baseUrl;

        return httpClient;
    }

    void IDisposable.Dispose()
    {
        _isDisposed = true;
        GC.SuppressFinalize(this);

        base.Dispose();
    }

    ~HttpClientHander()
    {
        if (_delayFinalization == DateTime.MinValue)
            _delayFinalization = DateTime.UtcNow;
        if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
            GC.ReRegisterForFinalize(this);
    }

    private static void SetupGlobalPoolFinalizer()
    {
        AppDomain.CurrentDomain.ProcessExit +=
            (sender, eventArgs) => { FinalizeGlobalPool(); };
    }

    private static void FinalizeGlobalPool()
    {
        foreach (var key in _uris)
        {
            HttpClientHander value = null;
            if (_httpClientsPool.TryGetValue(key, out value))
                try { value.Dispose(); } catch { }
        }

        _uris.Clear();
        _httpClientsPool = null;
    }
}

var handler = HttpClientHander.GetHttpClientHandle (новий Uri ("базовий URL")).

  • HttpClient як інтерфейс не може викликати Dispose ().
  • Утилізатор () буде викликаний сміттєзбірником неспішно. Або коли програма очищає об'єкт через його деструктор.
  • Використовує слабкі посилання + затримка логіки очищення, тому вона залишається у використанні до тих пір, поки її часто використовують.
  • Він виділяє лише новий HttpClient для кожної базової URL-адреси, переданої до нього. Причини, пояснені відповіддю Охада Шнайдера нижче. Погана поведінка при зміні базового URL-адреси.
  • HttpClientHandle дозволяє знущатися в тестах

Ідеально. Я бачу, що ви запускаєте Disposeметод, який ви реєструєте в GC. Це слід оцінювати вище у верхній частині.
Jeson Martajaya

Зауважте, що HttpClient здійснює об'єднання ресурсів за базовою URL-адресою. Отже, якщо ви потрапляєте у тисячі різних веб-сайтів у списку, ваша ефективність знизиться без очищення цих окремих сайтів. Це відкриває можливість розпорядження кожною базовою URL-адресою. Однак якщо ви використовуєте лише один веб-сайт, виклик розпорядження може бути лише з академічних причин.
TamusJRoyce

1

Використання ін'єкцій залежностей у вашому конструкторі полегшує управління всім терміном експлуатації HttpClient- виводить керуючий протягом життя поза кодом, який йому потрібен, і робить його легко змінювати на більш пізньому терміні.

Мої поточні переваги - це створити окремий клієнтський клас клієнта, який успадковує HttpClientодин раз на цільовий домен кінцевої точки, а потім зробити його однократним за допомогою введення залежності.public class ExampleHttpClient : HttpClient { ... }

Потім я приймаю залежність конструктора від користувацького http-клієнта в класах обслуговування, де мені потрібен доступ до цього API. Це вирішує проблему з життям і має переваги, коли справа стосується об'єднання з'єднань.

Ви можете ознайомитись із відпрацьованим прикладом у відповідній відповіді на https://stackoverflow.com/a/50238944/3140853


0

Будь ласка, прочитайте мою відповідь на дуже подібне запитання, розміщене нижче. Повинно бути зрозуміло, що слід лікуватиHttpClient екземплярів як до одиночних клавіш і повторно використовуватися для запитів.

Яка витрата на створення нового HttpClient за виклик у клієнта WebAPI?


-2

Я думаю, що слід використовувати однотонний шаблон, щоб уникнути необхідності створювати екземпляри HttpClient і постійно закривати його. Якщо ви використовуєте .Net 4.0, ви можете використовувати зразок коду, як показано нижче. для отримання додаткової інформації про однотонну схему перевірити тут .

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

Використовуйте код, як показано нижче.

var client = HttpClientSingletonWrapper.Instance;

3
Що - то , щоб стежити за тим, коли робить це (і інші подібні схеми): « Всі члени примірника не гарантується потокобезпечна. »
TNE

2
Правильна чи ні, ця відповідь повинна повністю залежати від того, якою програмою є те, з якого ви хочете використовувати HttpClient. Якщо у вас є веб-додаток і створіть HttpClient, що надсилається всім веб-запитам, ви зможете поділитися з ними, ви потенційно отримаєте безліч винятків підключення (залежно від того, наскільки популярний ваш веб-сайт! :-)). (Дивіться відповідь Девіда
Файвре
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.