Как стать автором
Обновить

Комментарии 17

Все мы что-нибудь да пулируем.))

Странно, что в статье про использование HttpClient в современном .NET нет ни слова про Socket Exhaustion, в свете чего совет "В современном .NET: использовать статический HttpClient экземпляр (или синглтон).." выглядит максимально неоднозначно.
@KoscheyScrag, если вдруг не знаком с проблемой - это важный момент, вот ссылки на источники на случай, если решишь изучить вопрос дополнить статью:

https://www.c-sharpcorner.com/article/httpclientfactory-vs-httpclient-to-implement-api-calls/

https://medium.com/c-sharp-progarmming/socket-exhaustion-problem-in-net-and-how-to-solve-it-92a186ddc628#:~:text=This is known as socket,major disruption to your service.

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

что касается использования статического экземпляра то субъективно предпочтительнее использовать IHttpClientFactory, но тем не менее это рабочий вариант, который в том числе рекомендует Майкрософт. Только надо помнить про DNS TTL и добавить в конструкторе SocketsHttpHandler с настройкой PooledConnectionLifetime.

Вечно теряюсь - сокет же открыт только с конкретным сайтом\сервером?

Т.е. рекомендация держать один httpClient - она про конкретное соединение с конкретным сервером и всё?

Условно, если речь о приложении, которое обращается к разным сайтам и с разными настройками (разные логины+пароли или прокси например), то для каждого сайта лучше всё таки иметь отдельный httpClient?

Да, иногда проще зарегистрировать сразу именованного http-клиента со всеми настройками, а потом просто инжектить куда надо.

builder.Services.AddHttpClient<FakeAPISender>((provider, client) => {
    client.BaseAddress = new Uri("http://fake");
    var auth = $"fake:fake";
    var enc = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", enc);

}).SetHandlerLifetime(TimeSpan.FromMinutes(5));

public class FakeAPISender
{
	private readonly HttpClient _httpClient;
	public FakeAPISender(HttpClient httpClient)
	{
		_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
	}
	 public async Task<DataResponse> GetData()
	 {
		var result = await _httpClient.GetAsync(_httpClient.BaseAddress?.ToString());
		.....
	 }
}

Спасибо, понял. И на мсдн сразу нашел информацию, когда вы написали про именованных клиентов =)

Покопаюсь ещё, как в таком клиенте лучше жить с куками и например необходимостью обновлять токены, но это уже мелочи.

если приложение обращается к разным сервисам с разными настройками, то лучше зарегистрировать под каждый такой сервис свой типизированный или сгенерированный клиент со своими настройками и через фабрику получать их. то есть ответ на ваш вопрос - да, будут отдельные клиенты. держать один httpCLient не означает что он во всем приложении должен быть одним единственным на всё и сразу, тут про то, что его стоит переиспользовать а не пересоздавать

Насколько я знаю, в современной реализации HttpClient код типа:

using(var client = new HttpClient())
{
   ....
}

вполне нормален и ничего плохого не сделает. Другое дело, что использовать new вместо DI это само по себе моветон.

Но вот за такое:

var res = client.SendAsync(req).Result;

действительно надо руки отрывать.

Ваши знания устарели, как раз такой код и приведёт к исчерпани лимита сокетов при высокой нагрузке

Хм... Поковырялся в исходниках и доках - вы действительно правы, а я ошибался. Они опять там поменяли. Этот несчастный HttpClient каждую версию переделывают. С другой стороны, лично мне все равно никогда бы не пришло в голову использовать HttpClient без DI, так что впросак с этим я бы все равно не попал.

конструкция using это же синтаксический сахар, так что в любой реализации (до сегодняшнего дня) код типа using(var client = new HttpClient()) не "нормален" для HttpClient'а =)

"В мире .NET есть два популярных nuget-пакета для работы с HTTP. Это RestSharp и Flurl.Http."

а как же Refit?

Так это тоже обёртка над HttpClient.

да, Refit отличная библиотека. как раз удобно для создания API клиентов для сервисов использовать:
https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/http-requests?view=aspnetcore-8.0#generated-clients

RestSharp как минималистичная альтернатива использованию HttpClient'а (но под капотом используется HttpClient все равно)

А почему бы вместо HttpClient не использовать библиотеку Refit, которая берет формирование, выполнение запросов, сериализацию и десериализацию данных на себя? Конфигурируешь httpclient как тебе надо через фабрику и дергаешь нужные методы

да, можно использовать Refit, это очень удобно для создания API клиентов.
на проекте планируем использовать этот способ в будущем. пока что типизированные клиенты делали

Зарегистрируйтесь на Хабре, чтобы оставить комментарий