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.
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 static
HttpClient
. 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 IHttpClientFactory
van 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
:
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 IHttpClientFactory
object 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 HttpMessageHandler
levensduur 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
HttpClient-richtlijnen voor .NET
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelinesHttpClientFactory gebruiken in .NET
https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factoryHttpClientFactory gebruiken in ASP.NET Core
https://learn.microsoft.com/aspnet/core/fundamentals/http-requestsHttpClientFactory-broncode in de
dotnet/runtime
GitHub-opslagplaats
https://github.com/dotnet/runtime/tree/release/7.0/src/libraries/Microsoft.Extensions.Http/Polly (.NET resilience and transient-fault-handling library)
https://thepollyproject.azurewebsites.net/