Delen via


IHttpClientFactory gebruiken om tolerante HTTP-aanvragen te implementeren

Tip

Deze inhoud is een fragment uit het eBook, .NET Microservices Architecture for Containerized .NET Applications, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.

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

IHttpClientFactory is een contract dat wordt geïmplementeerd door DefaultHttpClientFactory, een adviesfabriek, beschikbaar sinds .NET Core 2.1, voor het maken van HttpClient exemplaren die in uw toepassingen moeten worden gebruikt.

Problemen met de oorspronkelijke HttpClient-klasse die beschikbaar is in .NET

De oorspronkelijke en bekende HttpClient klasse kan eenvoudig worden gebruikt, maar in sommige gevallen wordt deze niet goed gebruikt door veel ontwikkelaars.

Hoewel deze klasse implementeert IDisposable, declaratie en instantiëren binnen een using instructie is niet de voorkeur omdat wanneer het HttpClient object wordt verwijderd, de onderliggende socket niet onmiddellijk wordt vrijgegeven, wat kan leiden tot een probleem met socketuitputting . Zie voor meer informatie over dit probleem het blogbericht You're using HttpClient wrong and it's destabilizing your software.

HttpClient Daarom is bedoeld om eenmalig te worden geïnstantieerd en hergebruikt gedurende de levensduur van een toepassing. Het instantiëren van een HttpClient klasse voor elke aanvraag zal het aantal sockets dat beschikbaar is onder zware belastingen uitputten. Dit probleem leidt tot SocketException fouten. Mogelijke benaderingen om dit probleem op te lossen, zijn gebaseerd op het maken van het HttpClient object als singleton of statisch, zoals wordt uitgelegd in dit Microsoft-artikel over httpClient-gebruik. Dit kan een goede oplossing zijn voor console-apps met korte levensduur of vergelijkbare, die een paar keer per dag worden uitgevoerd.

Een ander probleem dat ontwikkelaars tegenkomen, is bij het gebruik van een gedeeld exemplaar van HttpClient in langlopende processen. In een situatie waarin de HttpClient wordt geïnstantieerd als een singleton of een statisch object, kan de DNS-wijzigingen niet worden verwerkt, zoals beschreven in dit probleem van de GitHub-opslagplaats dotnet/runtime.

Het probleem is echter niet echt per HttpClient se, maar met de standaardconstructor voor HttpClient, omdat er een nieuw concreet exemplaar van HttpMessageHandlerwordt gemaakt, namelijk het exemplaar met socketsuitputting en DNS-wijzigingen die hierboven worden genoemd.

Om de hierboven genoemde problemen op te lossen en instanties beheerbaar te maken HttpClient , heeft .NET Core 2.1 twee benaderingen geïntroduceerd, een van deze IHttpClientFactorymethoden. Het is een interface die wordt gebruikt voor het configureren en maken HttpClient van exemplaren in een app via afhankelijkheidsinjectie (DI). Het biedt ook extensies voor op Polly gebaseerde middleware om te profiteren van het delegeren van handlers in HttpClient.

Het alternatief is om te gebruiken SocketsHttpHandler met geconfigureerd PooledConnectionLifetime. Deze benadering wordt toegepast op langdurige of singleton-exemplaren staticHttpClient . Zie HttpClient-richtlijnen voor .NET voor meer informatie over verschillende strategieën.

Polly is een tijdelijke bibliotheek voor foutafhandeling waarmee ontwikkelaars tolerantie aan hun toepassingen kunnen toevoegen door een aantal vooraf gedefinieerde beleidsregels op een vloeiende en threadveilige manier te gebruiken.

Voordelen van het gebruik van IHttpClientFactory

De huidige implementatie van IHttpClientFactory, die ook implementeert IHttpMessageHandlerFactory, biedt de volgende voordelen:

  • Biedt een centrale locatie voor het benoemen en configureren van logische HttpClient objecten. U kunt bijvoorbeeld een client (serviceagent) configureren die vooraf is geconfigureerd voor toegang tot een specifieke microservice.
  • Codificeer het concept van uitgaande middleware via het delegeren van handlers in HttpClient en het implementeren van op Polly gebaseerde middleware om te profiteren van het beleid voor tolerantie van Polly.
  • HttpClient heeft al het concept van het delegeren van handlers die kunnen worden gekoppeld voor uitgaande HTTP-aanvragen. U kunt HTTP-clients registreren bij de factory en u kunt een Polly-handler gebruiken om Polly-beleid te gebruiken voor opnieuw proberen, CircuitBreakers, enzovoort.
  • Beheer de levensduur van het voorkomen van de genoemde problemen/problemen die kunnen optreden bij het beheren HttpClient van HttpMessageHandler de levensduur zelf.

Tip

De HttpClient instanties die door DI worden geïnjecteerd, kunnen veilig worden verwijderd, omdat de bijbehorende HttpMessageHandler worden beheerd door de fabriek. Geïnjecteerde HttpClient exemplaren zijn tijdelijk vanuit het perspectief van een DI, terwijl HttpMessageHandler exemplaren kunnen worden beschouwd als Scoped. HttpMessageHandler exemplaren hebben hun eigen DI-bereiken, gescheiden van de toepassingsbereiken (bijvoorbeeld ASP.NET binnenkomende aanvraagbereiken). Zie HttpClientFactory gebruiken in .NET voor meer informatie.

Notitie

De implementatie van IHttpClientFactory (DefaultHttpClientFactory) is nauw gekoppeld aan de DI-implementatie in het Microsoft.Extensions.DependencyInjection NuGet-pakket. Als u zonder DI of met andere DI-implementaties wilt gebruiken, kunt u overwegen om een static of singleton HttpClient te gebruiken HttpClient bij PooledConnectionLifetime het instellen. Zie HttpClient-richtlijnen voor .NET voor meer informatie.

Meerdere manieren om IHttpClientFactory te gebruiken

Er zijn verschillende manieren waarop u in uw toepassing kunt gebruiken IHttpClientFactory :

  • Basaal gebruik
  • Benoemde clients gebruiken
  • Getypte clients gebruiken
  • Gegenereerde clients gebruiken

In deze richtlijnen ziet u de meest gestructureerde manier om te gebruiken, namelijk het gebruik IHttpClientFactoryvan getypte clients (serviceagentpatroon). Alle opties worden echter gedocumenteerd en worden momenteel vermeld in dit artikel over het IHttpClientFactory gebruik.

Notitie

Als uw app cookies vereist, is het misschien beter om het gebruik IHttpClientFactory in uw app te voorkomen. Zie Richtlijnen voor het gebruik van HTTP-clients voor alternatieve manieren om clients te beheren

Getypte clients gebruiken met IHttpClientFactory

Wat is een 'Getypte client'? Het is alleen een HttpClient die vooraf is geconfigureerd voor een bepaald gebruik. Deze configuratie kan specifieke waarden bevatten, zoals de basisserver, HTTP-headers of time-outs.

In het volgende diagram ziet u hoe getypte clients worden gebruikt met IHttpClientFactory:

Diagram showing how typed clients are used with IHttpClientFactory.

Afbeelding 8-4. Gebruiken IHttpClientFactory met getypte clientklassen.

In de bovenstaande afbeelding maakt een ClientService (gebruikt door een controller of clientcode) gebruik van een HttpClient gemaakt door de geregistreerde IHttpClientFactory. Deze fabriek wijst een HttpMessageHandler van een pool toe aan de HttpClient. De HttpClient kan worden geconfigureerd met polly's beleid bij het registreren van de IHttpClientFactory in de DI-container met de extensiemethode AddHttpClient.

Als u de bovenstaande structuur wilt configureren, voegt u uw toepassing toe IHttpClientFactory door het Microsoft.Extensions.Http NuGet-pakket te installeren dat de AddHttpClient extensiemethode voor IServiceCollectionbevat. Deze uitbreidingsmethode registreert de interne DefaultHttpClientFactory klasse die moet worden gebruikt als een singleton voor de interface IHttpClientFactory. Het definieert een tijdelijke configuratie voor de HttpMessageHandlerBuilder. Deze berichthandler (HttpMessageHandler object), afkomstig uit een pool, wordt gebruikt door de HttpClient geretourneerde uit de fabriek.

In het volgende codefragment ziet u hoe AddHttpClient() u getypte clients (serviceagents) kunt registreren die moeten worden gebruikt 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>();

Als u de clientservices registreert zoals wordt weergegeven in het vorige codefragment, wordt het DefaultClientFactory maken van een standaard HttpClient voor elke service. De getypte client wordt geregistreerd als tijdelijk met DI-container. In de voorgaande code AddHttpClient() registreert u CatalogService, BasketService, OrderingService als tijdelijke services, zodat ze direct kunnen worden geïnjecteerd en gebruikt zonder dat er extra registraties nodig zijn.

U kunt ook exemplaarspecifieke configuratie in de registratie toevoegen om bijvoorbeeld het basisadres te configureren en enkele beleidsregels voor tolerantie toe te voegen, zoals wordt weergegeven in het volgende:

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

In dit volgende voorbeeld ziet u de configuratie van een van de bovenstaande beleidsregels:

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

Meer informatie over het gebruik van Polly vindt u in het volgende artikel.

Levensduur van HttpClient

Telkens wanneer u een HttpClient object van het IHttpClientFactoryobject krijgt, wordt er een nieuw exemplaar geretourneerd. Maar elk HttpClient maakt gebruik van een HttpMessageHandler pool en hergebruikt door het IHttpClientFactory om het resourceverbruik te verminderen, zolang de HttpMessageHandlerlevensduur niet is verlopen.

Pooling van handlers is wenselijk omdat elke handler doorgaans zijn eigen onderliggende HTTP-verbindingen beheert; het maken van meer handlers dan nodig is, kan leiden tot verbindingsvertragingen. Sommige handlers houden verbindingen ook voor onbepaalde tijd open, waardoor de handler niet kan reageren op DNS-wijzigingen.

De HttpMessageHandler objecten in de pool hebben een levensduur die de tijdsduur is die een HttpMessageHandler exemplaar in de pool opnieuw kan gebruiken. De standaardwaarde is twee minuten, maar kan worden overschreven per getypte client. Als u deze wilt overschrijven, roept SetHandlerLifetime() u de IHttpClientBuilder geretourneerde aan bij het maken van de client, zoals wordt weergegeven in de volgende code:

//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));

Elke getypte client kan een eigen geconfigureerde handler-levensduurwaarde hebben. Stel de levensduur in om de verlooptijd van de handler uit te InfiniteTimeSpan schakelen.

Implementeer uw getypte clientklassen die gebruikmaken van de geïnjecteerde en geconfigureerde HttpClient

Als vorige stap moet u de getypte clientklassen definiëren, zoals de klassen in de voorbeeldcode, zoals 'BasketService', 'CatalogService', 'OrderingService', enzovoort: een getypte client is een klasse die een HttpClient object accepteert (geïnjecteerd via de constructor) en deze gebruikt om een externe HTTP-service aan te roepen. Voorbeeld:

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

De getypte client (CatalogService in het voorbeeld) wordt geactiveerd door DI (Afhankelijkheidsinjectie), wat betekent dat deze elke geregistreerde service in de constructor kan accepteren, naast HttpClient.

Een getypte client is in feite een tijdelijk object, wat betekent dat er telkens een nieuw exemplaar wordt gemaakt wanneer er een nodig is. Telkens wanneer deze wordt gebouwd, ontvangt deze een nieuw HttpClient exemplaar. De HttpMessageHandler objecten in de groep zijn echter de objecten die opnieuw worden gebruikt door meerdere HttpClient exemplaren.

Uw getypte clientklassen gebruiken

Als u uw getypte klassen hebt geïmplementeerd, kunt u ze laten registreren en configureren met AddHttpClient(). Daarna kunt u ze gebruiken waar services worden geïnjecteerd door DI, zoals in Razor-paginacode of een MVC-web-appcontroller, weergegeven in de onderstaande code van 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
        }

        }
}

Tot nu toe toont het bovenstaande codefragment alleen het voorbeeld van het uitvoeren van reguliere HTTP-aanvragen. Maar de 'magic' wordt geleverd in de volgende secties waarin wordt getoond hoe alle HTTP-aanvragen die zijn gedaan door HttpClient tolerant beleid kunnen hebben, zoals nieuwe pogingen met exponentieel uitstel, circuitonderbrekers, beveiligingsfuncties met behulp van verificatietokens of zelfs een andere aangepaste functie. En al deze kunnen worden gedaan door beleid toe te voegen en handlers te delegeren aan uw geregistreerde getypte clients.

Aanvullende bronnen