Použití IHttpClientFactory k implementaci odolných požadavků HTTP

Tip

Tento obsah je výňatek z eBooku, architektury mikroslužeb .NET pro kontejnerizované aplikace .NET, které jsou k dispozici na .NET Docs nebo jako zdarma ke stažení PDF, které lze číst offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

IHttpClientFactory je kontrakt implementovaný objektem DefaultHttpClientFactorypro vytváření názorů, který je k dispozici od .NET Core 2.1 pro vytváření HttpClient instancí, které se mají použít ve vašich aplikacích.

Problémy s původní třídou HttpClient dostupnou v .NET

Původní a dobře známou HttpClient třídu lze snadno použít, ale v některých případech ji mnoho vývojářů správně nepoužívá.

Ačkoli tato třída implementuje IDisposable, deklarování a vytvoření instance v rámci using příkazu není upřednostňované, protože když HttpClient se objekt odstraní, podkladový soket není okamžitě uvolněn, což může vést k problému vyčerpání soketu. Další informace o tomto problému najdete v blogovém příspěvku , který používáte httpClient, a je to nestabilita vašeho softwaru.

HttpClient Proto je určen k vytvoření instance jednou a po celou dobu životnosti aplikace znovu použít. HttpClient Vytvoření instance třídy pro každou žádost vyčerpá počet soketů dostupných při velkém zatížení. Výsledkem tohoto problému budou SocketException chyby. Možné přístupy k vyřešení tohoto problému jsou založeny na vytvoření objektu HttpClient jako singleton nebo statické, jak je vysvětleno v tomto článku Microsoftu o použití HttpClient. Může to být vhodné řešení pro krátkodobé konzolové aplikace nebo podobné, které běží několikrát denně.

Dalším problémem, na který narazí vývojáři, je použití sdílené instance dlouhotrvajících HttpClient procesů. V situaci, kdy se HttpClient vytvoří instance jako jeden nebo statický objekt, se nepodaří zpracovat změny DNS, jak je popsáno v tomto problému úložiště GitHubu dotnet/runtime.

Tento problém ale ve skutečnosti HttpClient není s každou sekundou, ale s výchozím konstruktorem pro HttpClient, protože vytvoří novou konkrétní instanci HttpMessageHandler, což je ten, který má vyčerpání soketů a problémy se změnami DNS uvedené výše.

Abychom vyřešili výše uvedené problémy a aby HttpClient bylo možné spravovat instance, zavedla .NET Core 2.1 dva přístupy, z nichž jedna je IHttpClientFactory. Jedná se o rozhraní, které se používá ke konfiguraci a vytváření HttpClient instancí v aplikaci prostřednictvím injektáže závislostí (DI). Poskytuje také rozšíření middlewaru založeného na Polly, která využívají delegování obslužných rutin v HttpClient.

Alternativou je použití SocketsHttpHandler s nakonfigurovaným PooledConnectionLifetime. Tento přístup se používá u dlouhotrvajících static nebo singletonových HttpClient instancí. Další informace o různých strategiích najdete v pokynech httpClient pro .NET.

Polly je knihovna pro zpracování přechodných chyb, která vývojářům pomáhá přidávat do svých aplikací odolnost pomocí některých předem definovaných zásad plynulým a bezpečným způsobem.

Výhody používání IHttpClientFactory

Současná implementace IHttpClientFactory, která také implementuje IHttpMessageHandlerFactory, nabízí následující výhody:

  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických HttpClient objektů. Můžete například nakonfigurovat klienta (agenta služby), který je předem nakonfigurovaný pro přístup ke konkrétní mikroslužbě.
  • Spoluvytvářet koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v HttpClient middlewaru založeném na Polly a implementaci middlewaru založeného na Polly pro zajištění odolnosti.
  • HttpClient již má koncept delegování obslužných rutin, které by mohly být propojeny pro odchozí požadavky HTTP. Klienty HTTP můžete zaregistrovat do továrny a pomocí obslužné rutiny Polly můžete použít zásady Polly pro opakování, CircuitBreakers atd.
  • Spravujte životnost HttpMessageHandler , abyste se vyhnuli uvedeným problémům nebo problémům, ke kterým může dojít při správě HttpClient životního cyklu sami.

Tip

Instance HttpClient vložené pomocí DI je možné bezpečně odstranit, protože přidružené HttpMessageHandler je spravováno továrnou. Vložené HttpClient instance jsou přechodné z hlediska DI, zatímco HttpMessageHandler instance mohou být považovány za Scoped. HttpMessageHandler instance mají vlastní obory DI, oddělené od oborů aplikace (například ASP.NET rozsahy příchozích požadavků). Další informace naleznete v tématu Použití HttpClientFactory v .NET.

Poznámka:

Implementace IHttpClientFactory (DefaultHttpClientFactory) je úzce svázaná s implementací DI v Microsoft.Extensions.DependencyInjection balíčku NuGet. Pokud potřebujete použít HttpClient bez DI nebo s jinýmiimplementacemi staticHttpClientPooledConnectionLifetime Další informace najdete v tématu HttpClient guidelines for .NET.

Několik způsobů použití IHttpClientFactory

Ve své aplikaci můžete použít IHttpClientFactory několik způsobů:

  • Základní použití
  • Použití pojmenovaných klientů
  • Použití typed clients
  • Použití vygenerovaných klientů

V zájmu stručnosti ukazuje tento návod nejstrukturovanější způsob použití IHttpClientFactory, což je použití typed clients (model agenta služby). Všechny možnosti jsou však zdokumentované a v současné době jsou uvedeny v tomto článku, který se zabývá používánímIHttpClientFactory.

Poznámka:

Pokud vaše aplikace vyžaduje soubory cookie, může být lepší se vyhnout použití IHttpClientFactory ve vaší aplikaci. Alternativní způsoby správy klientů najdete v pokynech pro používání klientů HTTP.

Použití typed clients with IHttpClientFactory

Co je tedy "Typový klient"? Je to jenom HttpClient předem nakonfigurovaná pro určité použití. Tato konfigurace může zahrnovat konkrétní hodnoty, jako je základní server, hlavičky HTTP nebo vypršení časového limitu.

Následující diagram znázorňuje, jak se používají IHttpClientFactorytypoví klienti:

Diagram showing how typed clients are used with IHttpClientFactory.

Obrázek 8–4 Použití IHttpClientFactory s typed client třídy.

Na obrázku ClientService výše používá HttpClient kód (používaný kontrolerem nebo kódem klienta) vytvořený registrovaným IHttpClientFactorykódem . Tato továrna HttpMessageHandler přiřadí z fondu k objektu HttpClient. Lze HttpClient nakonfigurovat pomocí zásad Polly při registraci IHttpClientFactory v kontejneru DI pomocí metody AddHttpClientrozšíření .

Pokud chcete nakonfigurovat výše uvedenou strukturu, přidejte IHttpClientFactory do aplikace instalaci Microsoft.Extensions.Http balíčku NuGet, který obsahuje metodu AddHttpClient rozšíření pro IServiceCollection. Tato metoda rozšíření zaregistruje interní DefaultHttpClientFactory třídu, která se má použít jako jednoúčelové rozhraní IHttpClientFactory. Definuje přechodnou konfiguraci pro HttpMessageHandlerBuilder. Tato obslužná rutina zprávy (HttpMessageHandler objekt), převzatá z fondu, je používána vrácenou HttpClient z továrny.

V dalším fragmentu kódu můžete zjistit, jak AddHttpClient() se dá použít k registraci klientů typu (agentů služeb), které potřebují používat 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>();

Registrace klientských služeb, jak je znázorněno v předchozím fragmentu kódu, vytvoří DefaultClientFactory pro každou službu standard HttpClient . Zadaný klient je zaregistrovaný jako přechodný v kontejneru DI. V předchozím kódu AddHttpClient() zaregistruje CatalogService, BasketService, OrderingService jako přechodné služby, aby je bylo možné vkládat a využívat přímo bez nutnosti dalších registrací.

Do registrace můžete přidat také konfiguraci specifickou pro instanci, například ke konfiguraci základní adresy a přidání některých zásad odolnosti, jak je znázorněno v následujících příkladech:

builder.Services.AddHttpClient<ICatalogService, CatalogService>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["BaseUrl"]);
})
    .AddPolicyHandler(GetRetryPolicy())
    .AddPolicyHandler(GetCircuitBreakerPolicy());

V tomto dalším příkladu uvidíte konfiguraci jedné z výše uvedených zásad:

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}

Další podrobnosti o používání Polly najdete v dalším článku.

Životnosti HttpClient

Pokaždé, když získáte objekt z objektu HttpClientIHttpClientFactory, je vrácena nová instance. Každý z nich HttpClient ale používá fond HttpMessageHandler , který se používá IHttpClientFactory k omezení spotřeby prostředků, pokud HttpMessageHandlernevypršela platnost životnosti.

Sdružování obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje vlastní základní připojení HTTP; vytváření více obslužných rutin, než je nutné, může vést ke zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS.

Objekty HttpMessageHandler ve fondu mají životnost, která je doba HttpMessageHandler , po kterou může být instance ve fondu znovu použita. Výchozí hodnota je dvě minuty, ale lze ji přepsat na typ klienta. Pokud ho chcete přepsat, zavolejte SetHandlerLifetime()IHttpClientBuilder vrácené při vytváření klienta, jak je znázorněno v následujícím kódu:

//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ždý typový klient může mít svou vlastní nakonfigurovanou hodnotu životnosti obslužné rutiny. Nastavte životnost tak, aby InfiniteTimeSpan se zakázala vypršení platnosti obslužné rutiny.

Implementace typed client tříd, které používají vložené a nakonfigurované HttpClient

Jako předchozí krok musíte mít definované třídy typed client, například třídy v ukázkovém kódu, jako je BasketService, CatalogService, OrderingService atd. – Typed Client je třída, která přijímá HttpClient objekt (vložený prostřednictvím jeho konstruktoru) a používá ho k volání některé vzdálené služby HTTP. Příklad:

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;
    }
}

Typed Client (CatalogService v příkladu) je aktivován di (injektáž závislostí), což znamená, že může přijmout jakoukoli registrovanou službu v jeho konstruktoru, kromě HttpClient.

Typový klient je v podstatě přechodný objekt, což znamená, že se vytvoří nová instance pokaždé, když je potřeba. Při každém vytvoření obdrží novou HttpClient instanci. HttpMessageHandler Objekty ve fondu jsou však objekty, které jsou opakovaně používány více HttpClient instancemi.

Použití tříd typed client

A konečně, jakmile máte zapsaných tříd implementované, můžete je zaregistrovat a nakonfigurovat s AddHttpClient(). Potom je můžete použít všude, kde jsou služby vloženy pomocí DI, například v kódu stránky Razor nebo kontroleru webové aplikace MVC, jak je znázorněno v následujícím kódu 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
        }

        }
}

Až do tohoto okamžiku výše uvedený fragment kódu ukazuje pouze příklad provádění běžných požadavků HTTP. "Magie" ale přichází v následujících částech, kde ukazuje, jak všechny požadavky HTTP vytvořené HttpClient pomocí odolných zásad, jako jsou opakování s exponenciálním zpoplatněním, jističem, funkcemi zabezpečení pomocí ověřovacích tokenů nebo dokonce jakoukoli jinou vlastní funkcí. A všechny tyto kroky je možné provést pouze přidáním zásad a delegováním obslužných rutin do registrovaných typed klientů.

Další materiály