Sdílet prostřednictvím


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

Spropitné

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.

obálka eBooku o architektuře mikroslužeb .NET pro kontejnerizované aplikace .NET.

IHttpClientFactory je kontrakt implementovaný službou DefaultHttpClientFactory, objektem pro vytváření instancí, které se mají používat ve vašich aplikacích, protože .NET Core 2.1 je k dispozici pro vytváření instancí HttpClient.

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

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

I když tato třída implementuje IDisposable, deklarování a instanci v rámci příkazu using není doporučeno, protože při odstranění objektu HttpClient není podkladový soket okamžitě uvolněn, což může vést k problému s vyčerpáním soketu . Další informace o tomto problému najdete v blogovém příspěvku Používáte HttpClient špatně a je to nestabilita vašeho softwaru.

Proto je HttpClient určeno k instanciaci jednou a opětovnému využití v průběhu životnosti aplikace. Vytvoření instance třídy HttpClient pro každý požadavek vyčerpá počet soketů dostupných při velkém zatížení. Tento problém bude mít za následek chyby SocketException. Možné přístupy k vyřešení tohoto problému jsou založeny na vytvoření objektu HttpClient jako singleton nebo statické proměnné, jak je vysvětleno v tomto článku Microsoft o použití HttpClientu. 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 HttpClient v dlouhotrvajících procesech. V situaci, kdy je HttpClient vytvořen jako instance singletonu nebo statický objekt, nezvládne zpracovat změny DNS, jak je popsáno v této záležitosti úložiště GitHub dotnet/runtime.

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

Abychom vyřešili výše uvedené problémy a aby HttpClient instance byly spravovatelné, zavedla platforma .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í instancí HttpClient v aplikaci prostřednictvím injektáže závislostí (DI). Poskytuje také rozšíření pro middleware založený na Polly, které umožňuje využití delegujících obslužných rutin v HttpClient.

Alternativou je použití SocketsHttpHandler s nakonfigurovaným PooledConnectionLifetime. Tento přístup se používá u dlouhodobých instancí static nebo instancí typu singleton HttpClient. Další informace o různých strategiích najdete v tématu pokyny 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

Aktuální 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ě.
  • Formalizujte koncept odchozího middlewaru delegováním obslužných rutin v HttpClient a implementací middlewaru založeného na Polly, abyste mohli využít politiky Polly pro zvýšení 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 opětovné pokusy, přerušovače okruhů atd.
  • Spravujte životnost HttpMessageHandler, abyste se vyhnuli uvedeným problémům, které mohou nastat při vlastní správě životnosti HttpClient.

Spropitné

Instance HttpClient vložené pomocí DI je možné bezpečně likvidovat, protože související HttpMessageHandler spravuje továrna. Vložené HttpClient instance jsou Přechodné z pohledu závislosti, zatímco HttpMessageHandler instance lze považovat za vymezené. HttpMessageHandler instance mají vlastní obory DI, oddělené od oborů aplikace (například rozsahy příchozích požadavků v ASP.NET). Další informace naleznete v tématu Použití HttpClientFactory v .NET.

Poznámka

Implementace IHttpClientFactory (DefaultHttpClientFactory) je úzce svázaná s implementací DI v balíčku NuGet Microsoft.Extensions.DependencyInjection. Pokud potřebujete použít HttpClient bez DI nebo s jinými implementacemi DI, zvažte použití static nebo singletonu HttpClient s nastaveným PooledConnectionLifetime. Další informace najdete v tématu pokyny HttpClient pro .NET.

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

V aplikaci můžete použít IHttpClientFactory několika způsoby:

  • Základní využití
  • Použití pojmenovaných klientů
  • Použití typovaných klientů
  • Použití vygenerovaných klientů

V zájmu stručnosti tento návod ukazuje nejstrukturovanější způsob používání IHttpClientFactory, což je použití typovaných klientů (vzorec agenta služby). Všechny možnosti jsou však zdokumentované a jsou nyní uvedeny v tomto článku, který se zabývá IHttpClientFactory využitím.

Poznámka

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

Jak používat typované klienty s IHttpClientFactory

Co je tedy "Typový klient"? Je to jen HttpClient, která je 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 použití typových klientů s IHttpClientFactory:

Diagram znázorňující, jak se typoví klienti používají s IHttpClientFactory

obrázek 8–4. Použití IHttpClientFactory s třídami typizovaného klienta.

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

Pokud chcete nakonfigurovat výše uvedenou strukturu, přidejte do aplikace IHttpClientFactory instalací balíčku NuGet Microsoft.Extensions.Http, který obsahuje metodu rozšíření AddHttpClient pro IServiceCollection. Tato metoda rozšíření zaregistruje třídu DefaultHttpClientFactory interní, která bude použita jako singleton pro rozhraní IHttpClientFactory. Definuje přechodnou konfiguraci pro HttpMessageHandlerBuilder. Tuto obslužnou rutinu zprávy (HttpMessageHandler objekt), převzatou z fondu, používá HttpClient, která byla vrácena z továrny.

V dalším úryvku zjistíte, jak lze AddHttpClient() použít k registraci typovaných klientů (Service Agents), kteří 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>();

Registrací klientských služeb, jak je znázorněno v předchozím fragmentu kódu, vytvoří DefaultClientFactory pro každou službu standardní HttpClient. Zadaný klient je zaregistrovaný jako přechodný v kontejneru DI. V předchozím kódu registruje AddHttpClient(), CatalogService, BasketService, OrderingService jako přechodné služby, aby mohly být vloženy a používány přímo, a to 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žití Polly najdete v příštím článku.

Životnost HttpClient

Pokaždé, když z IHttpClientFactoryzískáte objekt HttpClient , vrátí se nová instance. Každý HttpClient ale používá HttpMessageHandler, který je ve fondu a znovu používán IHttpClientFactory, aby se snížila spotřeba prostředků, dokud nevypršela platnost životnosti HttpMessageHandler.

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í dobu životnosti, po kterou lze opakovaně použít instanci HttpMessageHandler ve fondu. Výchozí hodnota je dvě minuty, ale může být upravena pro jednotlivé typy klientů. Pokud chcete nastavení přepsat, zavolejte SetHandlerLifetime() na IHttpClientBuilder, který je vrácen při vytváření klienta, jak ukazuje následující kód:

//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ý typizovaný klient může disponovat vlastní nakonfigurovanou hodnotou životnosti obslužné rutiny. Nastavte životnost na InfiniteTimeSpan pro deaktivaci vypršení platnosti obslužné rutiny.

Implementujte své třídy Typed Client, které používají vložený a nakonfigurovaný HttpClient

Jako předchozí krok musíte mít definované třídy typed client, jako jsou třídy v ukázkovém kódu, jako je BasketService, CatalogService, OrderingService atd. – Typový klient je třída, která přijímá objekt HttpClient (vložený prostřednictvím jeho konstruktoru) a používá ho k volání některé vzdálené služby HTTP. Napří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 (injektování závislostí), což znamená, že může přijmout jakoukoli registrovanou službu ve svém konstruktoru, včetně 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í instance obdrží novou instanci HttpClient. Objekty HttpMessageHandler ve fondu jsou však ty, které jsou používány opakovaně více instancemi HttpClient.

Použijte své třídy Typed Client

A konečně, jakmile máte implementované zapsané třídy, 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 můžou mít odolné zásady, jako jsou opakování s exponenciálním zpožděním, obvodové přerušovače, bezpečnostní funkce využívající ověřovací tokeny nebo dokonce jakékoli jiné vlastní funkce. A všechny tyto kroky je možné jednoduše provést přidáním zásad a delegováním obslužných rutin do registrovaných typovaných klientů.

Další zdroje informací