Używanie elementu IHttpClientFactory do implementowania odpornych na błędy żądań HTTP
Napiwek
Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.
IHttpClientFactory jest kontraktem implementowanym przez DefaultHttpClientFactory
fabrykę z opiniami dostępną od wersji .NET Core 2.1 na potrzeby tworzenia HttpClient wystąpień do użycia w aplikacjach.
Problemy z oryginalną klasą HttpClient dostępną na platformie .NET
Oryginalna i dobrze znana HttpClient klasa może być łatwo używana, ale w niektórych przypadkach nie jest ona prawidłowo używana przez wielu deweloperów.
Mimo że ta klasa implementuje IDisposable
metodę , deklarując i tworząc wystąpienie w using
instrukcji, nie jest preferowana, ponieważ gdy HttpClient
obiekt zostanie usunięty, bazowe gniazdo nie zostanie natychmiast zwolnione, co może prowadzić do problemu z wyczerpaniem gniazd. Aby uzyskać więcej informacji na temat tego problemu, zobacz wpis w blogu Używasz błędu HttpClient i destabilizuje oprogramowanie.
HttpClient
W związku z tym ma zostać utworzone wystąpienie raz i ponownie użyte przez cały czas stosowania. HttpClient
Utworzenie wystąpienia klasy dla każdego żądania spowoduje wyczerpanie liczby gniazd dostępnych pod dużym obciążeniem. Ten problem spowoduje SocketException
błędy. Możliwe podejścia do rozwiązania tego problemu są oparte na tworzeniu HttpClient
obiektu jako pojedynczego lub statycznego, jak wyjaśniono w tym artykule firmy Microsoft na temat użycia klienta HttpClient. Może to być dobre rozwiązanie dla krótkotrwałych aplikacji konsolowych lub podobnych, które działają kilka razy dziennie.
Innym problemem, który deweloperzy napotkają, jest użycie udostępnionego HttpClient
wystąpienia w długotrwałych procesach. W sytuacji, gdy obiekt HttpClient jest tworzone jako pojedynczy lub statyczny obiekt, nie może obsłużyć zmian DNS zgodnie z opisem w tym problemie repozytorium GitHub dotnet/runtime.
Jednak problem nie jest naprawdę związany z HttpClient
per se, ale z domyślnym konstruktorem httpClient, ponieważ tworzy nowe konkretne wystąpienie HttpMessageHandlerprogramu , czyli to, który ma gniazda wyczerpania i problemy z zmianami DNS wymienione powyżej.
Aby rozwiązać wymienione powyżej problemy i umożliwić HttpClient
zarządzanie wystąpieniami, platforma .NET Core 2.1 wprowadziła dwa podejścia— jedna z nich to IHttpClientFactory. Jest to interfejs używany do konfigurowania i tworzenia HttpClient
wystąpień w aplikacji za pomocą wstrzykiwania zależności (DI). Udostępnia również rozszerzenia oprogramowania pośredniczącego opartego na usłudze Polly, aby móc korzystać z delegowania programów obsługi w programie HttpClient.
Alternatywą jest użycie z skonfigurowanym PooledConnectionLifetime
programem SocketsHttpHandler
. Takie podejście jest stosowane do długotrwałych static
wystąpień lub pojedynczych HttpClient
wystąpień. Aby dowiedzieć się więcej na temat różnych strategii, zobacz Wskazówki dotyczące klienta HttpClient dla platformy .NET.
Polly to biblioteka obsługująca błędy przejściowe, która ułatwia deweloperom dodawanie odporności do swoich aplikacji przy użyciu wstępnie zdefiniowanych zasad w sposób płynny i bezpieczny wątkowo.
Zalety korzystania z elementu IHttpClientFactory
Bieżąca implementacja programu IHttpClientFactory, która również implementuje IHttpMessageHandlerFactoryprogram , oferuje następujące korzyści:
- Zapewnia centralną lokalizację nazewnictwa i konfigurowania obiektów logicznych
HttpClient
. Można na przykład skonfigurować klienta (agenta usługi), który jest wstępnie skonfigurowany do uzyskiwania dostępu do określonej mikrousługi. - Kodyfikują koncepcję wychodzącego oprogramowania pośredniczącego poprzez delegowanie programów obsługi w
HttpClient
oprogramowaniu pośredniczącym opartym na usłudze Polly i implementowanie oprogramowania pośredniczącego opartego na usłudze Polly w celu skorzystania z zasad Polly w celu zapewnienia odporności. HttpClient
Ma już koncepcję delegowania procedur obsługi, które mogą być połączone ze sobą dla wychodzących żądań HTTP. Można zarejestrować klientów HTTP w fabryce i użyć programu obsługi Polly do używania zasad Polly dla ponawiania prób, obwodów i tak dalej.- Zarządzaj okresem istnienia, HttpMessageHandler aby uniknąć wymienionych problemów/problemów, które mogą wystąpić podczas samodzielnego zarządzania
HttpClient
okresami istnienia.
Napiwek
HttpClient
Wystąpienia wprowadzone przez di można bezpiecznie usunąć, ponieważ skojarzony z nim HttpMessageHandler
jest zarządzany przez fabrykę. Iniekcyjne HttpClient
wystąpienia są przejściowe z perspektywy di, podczas gdy HttpMessageHandler
wystąpienia mogą być traktowane jako Zakres. HttpMessageHandler
wystąpienia mają własne zakresy di, oddzielone od zakresów aplikacji (na przykład ASP.NET zakresów żądań przychodzących). Aby uzyskać więcej informacji, zobacz Using HttpClientFactory in .NET (Używanie elementu HttpClientFactory na platformie .NET).
Uwaga
Implementacja IHttpClientFactory
(DefaultHttpClientFactory
) jest ściśle powiązana z implementacją di w Microsoft.Extensions.DependencyInjection
pakiecie NuGet. Jeśli musisz używać HttpClient
bez di lub z innymi implementacjami di, rozważ użycie pojedynczego static
elementu HttpClient
lub z konfiguracją PooledConnectionLifetime
. Aby uzyskać więcej informacji, zobacz Wskazówki dotyczące klienta HttpClient dla platformy .NET.
Wiele sposobów używania elementu IHttpClientFactory
Istnieje kilka sposobów użycia IHttpClientFactory
w aplikacji:
- Podstawowy sposób użycia
- Używanie nazwanych klientów
- Korzystanie z typowanych klientów
- Korzystanie z wygenerowanych klientów
Ze względu na zwięzłość te wskazówki pokazują najbardziej ustrukturyzowany sposób korzystania z IHttpClientFactory
programu , który polega na użyciu typu klientów (wzorzec agenta usługi). Jednak wszystkie opcje są udokumentowane i są obecnie wymienione w tym artykule obejmującym IHttpClientFactory
użycie.
Uwaga
Jeśli aplikacja wymaga plików cookie, lepszym rozwiązaniem może być unikanie korzystania z IHttpClientFactory aplikacji. Aby uzyskać alternatywne sposoby zarządzania klientami, zobacz Wytyczne dotyczące korzystania z klientów HTTP
Jak używać typowanych klientów z IHttpClientFactory
Co to jest "Typd Client"? Jest to tylko HttpClient
element wstępnie skonfigurowany do określonego użycia. Ta konfiguracja może zawierać określone wartości, takie jak serwer podstawowy, nagłówki HTTP lub przekroczenia limitu czasu.
Na poniższym diagramie przedstawiono sposób użycia klientów typu z usługą IHttpClientFactory
:
Rysunek 8–4. Używanie z IHttpClientFactory
typowymi klasami klienta.
Na powyższej ilustracji ClientService
obiekt (używany przez kontroler lub kod klienta) używa HttpClient
elementu utworzonego przez zarejestrowany IHttpClientFactory
obiekt . Ta fabryka przypisuje element HttpMessageHandler
z puli do obiektu HttpClient
. Można HttpClient
je skonfigurować przy użyciu zasad polly podczas rejestrowania IHttpClientFactory
w kontenerze DI za pomocą metody AddHttpClientrozszerzenia .
Aby skonfigurować powyższą strukturę, dodaj IHttpClientFactory aplikację, instalując Microsoft.Extensions.Http
pakiet NuGet zawierający metodę AddHttpClient rozszerzenia dla IServiceCollectionprogramu . Ta metoda rozszerzenia rejestruje klasę wewnętrzną DefaultHttpClientFactory
, która ma być używana jako pojedynczy element dla interfejsu IHttpClientFactory
. Definiuje on konfigurację przejściową dla elementu HttpMessageHandlerBuilder. Ta procedura obsługi komunikatów (HttpMessageHandler obiekt), pobrana z puli, jest używana przez HttpClient
obiekt zwrócony z fabryki.
W następnym fragmencie kodu można zobaczyć, jak AddHttpClient()
można użyć do rejestrowania klientów typu (agentów usługi), które muszą używać programu HttpClient
.
// Program.cs
//Add http client services at ConfigureServices(IServiceCollection services)
builder.Services.AddHttpClient<ICatalogService, CatalogService>();
builder.Services.AddHttpClient<IBasketService, BasketService>();
builder.Services.AddHttpClient<IOrderingService, OrderingService>();
Zarejestrowanie usług klienckich, jak pokazano w poprzednim fragmencie kodu, powoduje utworzenie standardu DefaultClientFactory
HttpClient
dla każdej usługi. Typowany klient jest zarejestrowany jako przejściowy w kontenerze DI. W poprzednim kodzie rejestruje usługę CatalogService, BasketService, OrderingService jako usługi przejściowe, AddHttpClient()
aby można je było wstrzyknąć i używać bezpośrednio bez konieczności dodatkowych rejestracji.
Możesz również dodać konfigurację specyficzną dla wystąpienia w rejestracji, aby na przykład skonfigurować adres podstawowy i dodać pewne zasady odporności, jak pokazano poniżej:
builder.Services.AddHttpClient<ICatalogService, CatalogService>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["BaseUrl"]);
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
W następnym przykładzie zobaczysz konfigurację jednej z powyższych zasad:
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
Więcej szczegółów na temat korzystania z usługi Polly można znaleźć w artykule Next (Dalej).
Okresy istnienia klienta HttpClient
Za każdym razem, gdy otrzymujesz HttpClient
obiekt z IHttpClientFactory
obiektu , zwracane jest nowe wystąpienie. Jednak każdy HttpClient
z nich używa puli HttpMessageHandler
i ponownego użycia przez IHttpClientFactory
element , aby zmniejszyć zużycie zasobów, o ile HttpMessageHandler
okres istnienia nie wygasł.
Buforowanie programów obsługi jest pożądane, ponieważ każda procedura obsługi zwykle zarządza własnymi podstawowymi połączeniami HTTP; utworzenie większej liczby procedur obsługi, niż jest to konieczne, może spowodować opóźnienia połączeń. Niektóre programy obsługi utrzymują również połączenia otwarte na czas nieokreślony, co może uniemożliwić programowi obsługi reagowanie na zmiany DNS.
Obiekty HttpMessageHandler
w puli mają okres istnienia, przez który HttpMessageHandler
można ponownie użyć wystąpienia w puli. Wartość domyślna to dwie minuty, ale można ją zastąpić na typizowanego klienta. Aby go zastąpić, wywołaj SetHandlerLifetime()
IHttpClientBuilder element zwrócony podczas tworzenia klienta, jak pokazano w poniższym kodzie:
//Set 5 min as the lifetime for the HttpMessageHandler objects in the pool used for the Catalog Typed Client
builder.Services.AddHttpClient<ICatalogService, CatalogService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Każdy klient typu może mieć własną skonfigurowaną wartość okresu istnienia programu obsługi. Ustaw okres istnienia, aby InfiniteTimeSpan
wyłączyć wygaśnięcie programu obsługi.
Zaimplementuj klasy klienta typu, które używają wstrzykniętego i skonfigurowanego klienta HttpClient
W poprzednim kroku należy zdefiniować klasy klienta typu, takie jak klasy w przykładowym kodzie, takie jak "BasketService", "CatalogService", "OrderingService", itp. — typdowany klient to klasa, która akceptuje HttpClient
obiekt (wstrzykiwany za pośrednictwem konstruktora) i używa go do wywoływania zdalnej usługi HTTP. Na przykład:
public class CatalogService : ICatalogService
{
private readonly HttpClient _httpClient;
private readonly string _remoteServiceBaseUrl;
public CatalogService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Catalog> GetCatalogItems(int page, int take,
int? brand, int? type)
{
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl,
page, take, brand, type);
var responseString = await _httpClient.GetStringAsync(uri);
var catalog = JsonConvert.DeserializeObject<Catalog>(responseString);
return catalog;
}
}
Typowany klient (CatalogService
w przykładzie) jest aktywowany przez di (wstrzykiwanie zależności), co oznacza, że może zaakceptować dowolną zarejestrowaną usługę w konstruktorze, oprócz HttpClient
.
Klient typu jest w rzeczywistości obiektem przejściowym, co oznacza, że nowe wystąpienie jest tworzone za każdym razem, gdy jest potrzebny. Otrzymuje nowe HttpClient
wystąpienie za każdym razem, gdy jest tworzone. HttpMessageHandler
Jednak obiekty w puli są obiektami, które są ponownie używane przez wiele HttpClient
wystąpień.
Używanie klas klienta typu
Na koniec po zaimplementowaniu klas wpisanych można je zarejestrować i skonfigurować za pomocą polecenia AddHttpClient()
. Następnie można używać ich wszędzie tam, gdzie usługi są wstrzykiwane przez di, na przykład w kodzie strony Razor lub kontrolerze aplikacji internetowej MVC, pokazanym w poniższym kodzie z eShopOnContainers:
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
public class CatalogController : Controller
{
private ICatalogService _catalogSvc;
public CatalogController(ICatalogService catalogSvc) =>
_catalogSvc = catalogSvc;
public async Task<IActionResult> Index(int? BrandFilterApplied,
int? TypesFilterApplied,
int? page,
[FromQuery]string errorMsg)
{
var itemsPage = 10;
var catalog = await _catalogSvc.GetCatalogItems(page ?? 0,
itemsPage,
BrandFilterApplied,
TypesFilterApplied);
//… Additional code
}
}
}
Do tego momentu powyższy fragment kodu pokazuje tylko przykład wykonywania regularnych żądań HTTP. Jednak "magia" jest dostępna w poniższych sekcjach, w których pokazano, jak wszystkie żądania HTTP wysyłane przez HttpClient
program mogą mieć odporne zasady, takie jak ponawianie prób z wykładniczym wycofywaniem, wyłącznikami, funkcjami zabezpieczeń przy użyciu tokenów uwierzytelniania, a nawet innymi funkcjami niestandardowymi. Wszystkie te czynności można wykonać tylko przez dodanie zasad i delegowanie programów obsługi do zarejestrowanych klientów wpisanych.
Dodatkowe zasoby
Wskazówki dotyczące klienta HttpClient dla platformy .NET
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelinesKorzystanie z elementu HttpClientFactory na platformie .NET
https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factoryUżywanie elementu HttpClientFactory w programie ASP.NET Core
https://learn.microsoft.com/aspnet/core/fundamentals/http-requestsKod źródłowy HttpClientFactory w
dotnet/runtime
repozytorium GitHub
https://github.com/dotnet/runtime/tree/release/7.0/src/libraries/Microsoft.Extensions.Http/Polly (odporność platformy.NET i biblioteka obsługi błędów przejściowych)
https://thepollyproject.azurewebsites.net/