HttpClient и HttpClientHandler должны быть расположены между запросами?

334

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

В usingзаявлении говорится:

Как правило, когда вы используете объект IDisposable, вы должны объявить и создать его экземпляр в операторе 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()ни явно, ни неявно. Например:

В комментариях к объявлению кто-то спросил сотрудника Microsoft:

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

Его ответ был:

В общем, это правильно, хотя вы должны быть осторожны с «использованием» и асинхронностью, поскольку они не смешиваются в .Net 4, в .Net 4.5 вы можете использовать «ожидание» внутри выражения «использование».

Кстати, вы можете повторно использовать один и тот же 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, вы должны объявить и создать его экземпляр в операторе using». Я всегда читал документацию по классу, реализующему IDisposable, прежде чем решить, стоит ли использовать для него использование. Как автор библиотек, в которых я внедряю IDisposable, потому что мне нужно освобождать неуправляемые ресурсы, я был бы в ужасе, если бы потребители создавали утилизированный экземпляр каждый раз вместо повторного использования существующего экземпляра. Нельзя сказать, что не распоряжайтесь экземпляром в конце концов ..
markmnl
1
Я отправил PR в Microsoft, чтобы обновить их документы: github.com/dotnet/docs/pull/2470
markmnl

Ответы:

259

По общему мнению, вам не нужно (не нужно) избавляться от HttpClient.

Многие люди, которые тесно связаны с тем, как это работает, заявили об этом.

См. Сообщение в блоге Даррела Миллера и соответствующий пост SO: сканирование HttpClient приводит к утечке памяти для справки.

Я также настоятельно рекомендую прочитать главу HttpClient из раздела Разработка веб-API Evolvable с 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объектов в идеале следует утилизировать, когда вы закончите с ними , особенно те, которые владеют именованными / общими ресурсами ОС . HttpClientНе исключение, поскольку, как указывает Даррел Миллер , он выделяет токены отмены, а тела запросов / ответов могут быть неуправляемыми потоками.
  2. Тем не менее, лучшая практика для HttpClient гласит, что вы должны создать один экземпляр и максимально использовать его (используя его многопоточные элементы в многопоточных сценариях). Поэтому в большинстве сценариев вы никогда не избавитесь от него просто потому, что он будет вам нужен постоянно .
  3. Проблема повторного использования одного и того же HttpClient «навсегда» заключается в том, что базовое HTTP-соединение может оставаться открытым с изначально разрешенным DNS-IP, независимо от изменений DNS . Это может быть проблемой в сценариях, таких как сине-зеленое развертывание и аварийное переключение на основе DNS . Существуют различные подходы к решению этой проблемы, наиболее надежный из которых заключается в том, что сервер отправляет Connection:closeзаголовок после изменения DNS. Другая возможность включает в себя повторное использование HttpClientна стороне клиента, либо периодически, либо с помощью какого-либо механизма, который узнает об изменении DNS. См. Https://github.com/dotnet/corefx/issues/11224 для получения дополнительной информации (я предлагаю внимательно прочитать ее, прежде чем вслепую использовать код, предложенный в связанном сообщении в блоге).
Охад Шнайдер
источник
Я располагаю им все время, так как не могу переключить прокси на экземпляре;)
ed22
Если вам по какой-либо причине необходимо утилизировать HttpClient, вы должны хранить статический экземпляр HttpMessageHandler, поскольку его устранение является причиной проблем, связанных с удалением HttpClient. HttpClient имеет перегрузку конструктора, которая позволяет вам указать, что поставляемый обработчик не должен удаляться, и в этом случае вы можете повторно использовать HttpMessageHandler с другими экземплярами HttpClient.
Том Линт
Вы должны держаться за свой HttpClient, но вы можете использовать что-то вроде System.Net.ServicePointManager.DnsRefreshTimeout = 3000; Это полезно, например, если вы используете мобильное устройство, которое в любое время может переключаться между Wi-Fi и 4G.
Йохан Франзен
18

В моем понимании, вызов Dispose()необходим только тогда, когда он блокирует ресурсы, которые вам понадобятся позже (например, конкретное соединение). Это всегда рекомендуется , чтобы освободить ресурсы , которые больше не используется, даже если вам не нужно их снова, просто потому , что вы не должны вообще быть держась за ресурсы , которые вы не используете (каламбур).

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

Но, как правило, когда класс реализует IDisposable, понимается, что вы должны Dispose()его экземпляры, как только вы полностью готовы и способны. Я бы сказал, что это особенно верно в тех случаях, HttpClientкогда явно не задокументировано, какие ресурсы или соединения удерживаются / открываются. В случае, когда соединение будет повторно использовано [скоро], вы захотите отказаться Dipose()от него - в этом случае вы не «полностью готовы».

См. Также: Метод IDisposable.Dispose и Когда вызывать Dispose.

svidgen
источник
7
Это как если бы кто-то принес банан в ваш дом, съел его и вытащил его с кожурой. Что они должны делать с кожурой? ... Если они выходят с ним за дверь, отпустите их. Если они торчат, заставьте их выбросить его в мусор, чтобы он не вонял.
svidgen
просто чтобы уточнить этот ответ, вы говорите: «Нет необходимости утилизировать, если программа завершится сразу после ее использования»? И что вы должны распоряжаться, если программа будет продолжаться в течение некоторого времени, делая другие вещи?
Фернандо Коррейя,
@FernandoCorreia Да, если я ничего не забуду, я думаю, что это безопасный принцип. Подумайте об этом в каждом случае. Например, если вы работаете с соединением, вы не хотите Dispose()преждевременно его устанавливать, и вам придется повторно подключаться через несколько секунд, если существующее соединение можно использовать повторно. Кроме того, вы не хотите излишне Dispose()изображения или других структур, вам может понадобиться восстановить через минуту или две.
svidgen
Я понимаю. Но в частном случае HttpClient и HttpClientHandler, о которых этот вопрос, они держат открытый ресурс, такой как соединение HTTP? Если это то, что происходит, мне, возможно, придется переосмыслить свой порядок их использования.
Фернандо Коррейя
1
@DPeden Ваш ответ совсем не противоречит моему. Обратите внимание, я сказал, что вы должны утилизировать () его экземпляры, как только вы полностью готовы и способны . Если вы планируете использовать экземпляр снова, вы не готовы .
svidgen
9

Dispose () вызывает приведенный ниже код, который закрывает соединения, открытые экземпляром HttpClient. Код был создан декомпиляцией с помощью dotPeek.

HttpClientHandler.cs - избавиться

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Если вы не вызываете dispose, то 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();
}

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

Тимоти Гонсалес
источник
1
Вы также можете увидеть код на GitHub. github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/...
TamusJRoyce
8

Краткий ответ: Нет, утверждение в принятом в настоящее время ответе НЕ является точным : «Общее мнение заключается в том, что вам не нужно (не нужно) избавляться от HttpClient».

Длинный ответ : ОБА из следующих утверждений верны и достижимы одновременно:

  1. «HttpClient предназначен для единовременного создания экземпляра и повторного использования в течение всего жизненного цикла приложения», цитируется в официальной документации .
  2. IDisposableОбъект должен / рекомендуется быть утилизированы.

И они НЕ ОБЯЗАТЕЛЬНО конфликтуют друг с другом. Это просто вопрос того, как вы организуете свой код для повторного использования HttpClientи по-прежнему правильно его используете.

Еще более длинный ответ процитирован из моего другого ответа :

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

Я полагаю, что это сводится к негласной (ошибочной?) Концепции: «ожидается, что IDisposable объект будет недолговечным» .

ОДНАКО, хотя это, конечно, выглядит недолгим, когда мы пишем код в этом стиле:

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

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

RayLuo
источник
спасибо за это объяснение. Это явно пролить свет на консенсус. С вашей точки зрения, когда вы думаете , что это собственно назвать HttpClient.Dispose?.
Джесон Мартаджая
@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);
    }
}

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

pcdev
источник
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 вы не можете очистить заголовки и добавлять новые перед каждым запросом?
Мандип Джанжуа
Microsoft также рекомендует повторно использовать экземпляры HttpClient
Mandeep Janjua
@MandeepJanjua в этом примере клиент выглядит как консольное приложение. Я имел в виду веб-приложение, являющееся клиентом.
Бытев
3

При обычном использовании (ответы <2 ГБ) нет необходимости утилизировать сообщения HttpResponseMessages.

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

  • Если вы читаете данные в byte [] (например, GetByteArrayAsync) или строку, все данные читаются, поэтому избавляться от необходимости не нужно.
  • Другие перегрузки будут по умолчанию читать поток до 2 ГБ (HttpCompletionOption - это ResponseContentRead, HttpClient.MaxResponseContentBufferSize по умолчанию - 2 ГБ)

Если для HttpCompletionOption задано значение ResponseHeadersRead или размер ответа превышает 2 ГБ, следует выполнить очистку. Это можно сделать, вызвав Dispose для HttpResponseMessage или вызвав Dispose / Close для потока, полученного из содержимого HttpResonseMessage, или полностью прочитав его.

Вызываете ли вы Dispose на 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 ().
  • Dispose () будет вызываться с задержкой сборщиком мусора. Или когда программа очищает объект через деструктор.
  • Использует слабые ссылки + отложенную логику очистки, поэтому она остается в использовании до тех пор, пока она часто используется повторно.
  • Он только выделяет новый HttpClient для каждого базового URL, переданного ему. Причины объяснил Охад Шнайдер ответ ниже. Плохое поведение при изменении базового URL.
  • HttpClientHandle позволяет Mocking в тестах
TamusJRoyce
источник
Отлично. Я вижу, что вы вызываете Disposeметод, который вы регистрируете в GC. Это должно быть оценено выше на вершине.
Джесон Мартаджая
Обратите внимание, что HttpClient выполняет пул ресурсов для каждого базового URL. Поэтому, если вы попадаете в список тысяч различных веб-сайтов, ваша производительность будет снижаться без очистки этих отдельных сайтов. Это предоставляет возможность распоряжаться каждым базовым URL-адресом. Тем не менее, если вы используете только один веб-сайт, вы можете позвонить только по академическим причинам.
TamusJRoyce
1

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

В настоящее время я предпочитаю создать отдельный клиентский класс http, который наследует HttpClientодин раз для целевого домена конечной точки, а затем сделать его одиночным с помощью внедрения зависимостей.public class ExampleHttpClient : HttpClient { ... }

Затем я беру зависимость конструктора от пользовательского http-клиента в классах обслуживания, где мне нужен доступ к этому API. Это решает проблему срока службы и имеет преимущества, когда речь идет о пуле соединений.

Вы можете увидеть обработанный пример в соответствующем ответе на https://stackoverflow.com/a/50238944/3140853

alastairtree
источник
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;
yayadavid
источник
3
Что - то , чтобы следить за тем, когда делает это (и другие подобные схемы): « Все члены экземпляра не гарантируется потокобезопасными. »
TNE
2
Будет ли этот ответ правильным или нет, должно полностью зависеть от того, из какого приложения вы хотите использовать HttpClient. Если у вас есть веб-приложение и вы создаете одноэлементный HttpClient, которым будут делиться все веб-запросы, вы потенциально получите множество исключений при подключении (в зависимости от того, насколько популярен ваш сайт! :-)). (См. Ответ
Дэвида Фэйвра