Använda IHttpClientFactory för att implementera elastiska HTTP-begäranden
Dricks
Det här innehållet är ett utdrag från eBook, .NET Microservices Architecture for Containerized .NET Applications, tillgängligt på .NET Docs eller som en kostnadsfri nedladdningsbar PDF som kan läsas offline.
IHttpClientFactory är ett kontrakt implementerat av DefaultHttpClientFactory
, en åsiktsfabrik, tillgänglig sedan .NET Core 2.1, för att skapa HttpClient instanser som ska användas i dina program.
Problem med den ursprungliga HttpClient-klassen som är tillgänglig i .NET
Den ursprungliga och välkända HttpClient klassen kan enkelt användas, men i vissa fall används den inte korrekt av många utvecklare.
Även om den här klassen implementerar IDisposable
, är det inte bra att deklarera och instansiera den i en using
-instruktion eftersom den underliggande socketen HttpClient
inte släpps omedelbart när objektet tas bort, vilket kan leda till ett socketöverbelastningsproblem . Mer information om det här problemet finns i blogginlägget Du använder HttpClient fel och det destabiliserar programvaran.
HttpClient
Därför är avsett att instansieras en gång och återanvändas under hela programmets livslängd. Om du instansierar en HttpClient
klass för varje begäran uttöms antalet tillgängliga socketar under tunga belastningar. Det här problemet resulterar i SocketException
fel. Möjliga metoder för att lösa problemet baseras på skapandet av objektet som singleton eller statiskt, enligt beskrivningen HttpClient
i den här Microsoft-artikeln om HttpClient-användning. Detta kan vara en bra lösning för kortlivade konsolappar eller liknande, som körs några gånger om dagen.
Ett annat problem som utvecklare stöter på är när de använder en delad instans av HttpClient
i långvariga processer. I en situation där HttpClient instansieras som en singleton eller ett statiskt objekt, kan den inte hantera DNS-ändringarna enligt beskrivningen i det här problemet med GitHub-lagringsplatsen dotnet/runtime.
Problemet är dock inte riktigt i HttpClient
sig, utan med standardkonstruktorn för HttpClient, eftersom det skapar en ny konkret instans av HttpMessageHandler, som är den som har problem med sockets-överbelastning och DNS-ändringar som nämns ovan.
För att åtgärda problemen ovan och för att göra HttpClient
instanser hanterbara introducerade .NET Core 2.1 två metoder, varav en är IHttpClientFactory. Det är ett gränssnitt som används för att konfigurera och skapa HttpClient
instanser i en app via beroendeinmatning (DI). Det innehåller även tillägg för Polly-baserade mellanprogram för att dra nytta av delegering av hanterare i HttpClient.
Alternativet är att använda SocketsHttpHandler
med konfigurerad PooledConnectionLifetime
. Den här metoden tillämpas på långlivade eller static
singleton-instanser HttpClient
. Mer information om olika strategier finns i HttpClient-riktlinjer för .NET.
Polly är ett tillfälligt bibliotek för felhantering som hjälper utvecklare att öka återhämtning i sina program genom att använda vissa fördefinierade principer på ett flytande och trådsäkert sätt.
Fördelar med att använda IHttpClientFactory
Den aktuella implementeringen av IHttpClientFactory, som också implementerar IHttpMessageHandlerFactory, erbjuder följande fördelar:
- Tillhandahåller en central plats för namngivning och konfigurering av logiska
HttpClient
objekt. Du kan till exempel konfigurera en klient (tjänstagent) som är förkonfigurerad för åtkomst till en specifik mikrotjänst. - Kodifiera begreppet utgående mellanprogram via att delegera hanterare i
HttpClient
och implementera Polly-baserade mellanprogram för att dra nytta av Pollys policyer för återhämtning. HttpClient
har redan konceptet att delegera hanterare som kan länkas samman för utgående HTTP-begäranden. Du kan registrera HTTP-klienter i fabriken och du kan använda en Polly-hanterare för att använda Polly-principer för återförsök, CircuitBreakers och så vidare.- Hantera livslängden HttpMessageHandler för för att undvika de problem/problem som kan uppstå när du hanterar
HttpClient
livslängden själv.
Dricks
De HttpClient
instanser som matas in av DI kan tas bort på ett säkert sätt eftersom de associerade HttpMessageHandler
hanteras av fabriken. Inmatade HttpClient
instanser är tillfälliga ur ett DI-perspektiv, medan HttpMessageHandler
instanser kan betraktas som begränsade. HttpMessageHandler
instanser har sina egna DI-omfång, åtskilda från programomfattningarna (till exempel ASP.NET inkommande omfång för begäran). Mer information finns i Använda HttpClientFactory i .NET.
Kommentar
Implementeringen av IHttpClientFactory
(DefaultHttpClientFactory
) är nära knuten till DI-implementeringen i Microsoft.Extensions.DependencyInjection
NuGet-paketet. Om du behöver använda HttpClient
utan DI eller med andra DI-implementeringar kan du överväga att använda en eller singleton static
HttpClient
med PooledConnectionLifetime
konfiguration. Mer information finns i HttpClient-riktlinjer för .NET.
Flera sätt att använda IHttpClientFactory
Det finns flera sätt att använda IHttpClientFactory
i ditt program:
- Grundläggande användning
- Använda namngivna klienter
- Använda inskrivna klienter
- Använda genererade klienter
För korthets skull visar den här vägledningen det mest strukturerade sättet att använda IHttpClientFactory
, vilket är att använda typade klienter (Service Agent-mönster). Alla alternativ är dock dokumenterade och visas för närvarande i den här artikeln som täcker IHttpClientFactory
användningen.
Kommentar
Om din app kräver cookies kan det vara bättre att undvika att använda IHttpClientFactory i din app. Alternativa sätt att hantera klienter finns i Riktlinjer för att använda HTTP-klienter
Använda inskrivna klienter med IHttpClientFactory
Vad är då en "typad klient"? Det är bara en HttpClient
som är förkonfigurerad för viss användning. Den här konfigurationen kan innehålla specifika värden som basservern, HTTP-huvuden eller tidsgränser.
Följande diagram visar hur typerade klienter används med IHttpClientFactory
:
Bild 8-4. Använda IHttpClientFactory
med typerade klientklasser.
I bilden ovan använder en ClientService
(som används av en styrenhet eller klientkod) en HttpClient
som skapats av den registrerade IHttpClientFactory
. Den här fabriken tilldelar en HttpMessageHandler
från en pool till HttpClient
. HttpClient
Kan konfigureras med Pollys principer när du IHttpClientFactory
registrerar i DI-containern med tilläggsmetoden AddHttpClient.
Om du vill konfigurera strukturen ovan lägger du till IHttpClientFactory i ditt program genom att installera NuGet-paketet Microsoft.Extensions.Http
som innehåller AddHttpClient tilläggsmetoden för IServiceCollection. Den här tilläggsmetoden registrerar den interna DefaultHttpClientFactory
klassen som ska användas som en singleton för gränssnittet IHttpClientFactory
. Den definierar en tillfällig konfiguration för HttpMessageHandlerBuilder. Den här meddelandehanteraren (HttpMessageHandler -objektet), som hämtas från en pool, används av den HttpClient
som returneras från fabriken.
I nästa kodfragment kan du se hur AddHttpClient()
du kan använda för att registrera typade klienter (tjänstagenter) som behöver använda 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>();
Genom att registrera klienttjänsterna enligt föregående kodfragment blir DefaultClientFactory
det en standard HttpClient
för varje tjänst. Den inskrivna klienten registreras som tillfällig med DI-container. I föregående kod AddHttpClient()
registrerar du CatalogService, BasketService, OrderingService som tillfälliga tjänster så att de kan matas in och användas direkt utan ytterligare registreringar.
Du kan också lägga till instansspecifik konfiguration i registreringen för att till exempel konfigurera basadressen och lägga till några återhämtningsprinciper, som du ser i följande:
builder.Services.AddHttpClient<ICatalogService, CatalogService>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["BaseUrl"]);
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
I nästa exempel kan du se konfigurationen av någon av ovanstående principer:
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
Mer information om hur du använder Polly finns i artikeln Nästa.
HttpClient-livslängd
Varje gång du hämtar ett HttpClient
objekt från IHttpClientFactory
returneras en ny instans. Men var HttpClient
och en använder en HttpMessageHandler
som är poolad och återanvänds av IHttpClientFactory
för att minska resursförbrukningen, så länge livslängden HttpMessageHandler
inte har upphört att gälla.
Poolning av hanterare är önskvärt eftersom varje hanterare vanligtvis hanterar sina egna underliggande HTTP-anslutningar. om du skapar fler hanterare än nödvändigt kan det leda till anslutningsfördröjningar. Vissa hanterare håller även anslutningarna öppna på obestämd tid, vilket kan hindra hanteraren från att reagera på DNS-ändringar.
Objekten HttpMessageHandler
i poolen har en livslängd som är den tid som en HttpMessageHandler
instans i poolen kan återanvändas. Standardvärdet är två minuter, men det kan åsidosättas per typerad klient. Om du vill åsidosätta den anropar SetHandlerLifetime()
du den IHttpClientBuilder som returneras när klienten skapas, enligt följande kod:
//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));
Varje typad klient kan ha ett eget konfigurerat hanterares livslängdsvärde. Ange livslängden till InfiniteTimeSpan
för att inaktivera hanterarens förfallodatum.
Implementera dina inskrivna klientklasser som använder den inmatade och konfigurerade HttpClient
Som ett föregående steg måste du ha definierat dina typade klientklasser, till exempel klasserna i exempelkoden, till exempel "BasketService", "CatalogService", "OrderingService" osv. – En typad klient är en klass som accepterar ett HttpClient
objekt (matas in via konstruktorn) och använder det för att anropa någon fjärr-HTTP-tjänst. Till exempel:
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;
}
}
Den inskrivna klienten (CatalogService
i exemplet) aktiveras av DI (beroendeinmatning), vilket innebär att den kan acceptera alla registrerade tjänster i konstruktorn, förutom HttpClient
.
En typad klient är i själva verket ett tillfälligt objekt, vilket innebär att en ny instans skapas varje gång en behövs. Den tar emot en ny HttpClient
instans varje gång den skapas. Objekten HttpMessageHandler
i poolen är dock de objekt som återanvänds av flera HttpClient
instanser.
Använda dina typerade klientklasser
När du har implementerat dina inskrivna klasser kan du få dem registrerade och konfigurerade med AddHttpClient()
. Därefter kan du använda dem där tjänster matas in av DI, till exempel i Razor-sidkod eller en MVC-webbappkontrollant, som visas i koden nedan från 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
}
}
}
Fram tills nu visar kodfragmentet ovan endast exemplet på att utföra vanliga HTTP-begäranden. Men "magin" finns i följande avsnitt där den visar hur alla HTTP-begäranden som görs av HttpClient
kan ha motståndskraftiga principer som återförsök med exponentiell backoff, kretsbrytare, säkerhetsfunktioner med autentiseringstoken eller till och med andra anpassade funktioner. Och alla dessa kan göras bara genom att lägga till principer och delegera hanterare till dina registrerade typade klienter.
Ytterligare resurser
HttpClient-riktlinjer för .NET
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelinesAnvända HttpClientFactory i .NET
https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factoryAnvända HttpClientFactory i ASP.NET Core
https://learn.microsoft.com/aspnet/core/fundamentals/http-requestsHttpClientFactory-källkod på
dotnet/runtime
GitHub-lagringsplatsen
https://github.com/dotnet/runtime/tree/release/7.0/src/libraries/Microsoft.Extensions.Http/Polly (.NET-motståndskraft och tillfälligt felhanteringsbibliotek)
https://thepollyproject.azurewebsites.net/
Feedback
https://aka.ms/ContentUserFeedback.
Kommer snart: Under hela 2024 kommer vi att fasa ut GitHub-problem som feedbackmekanism för innehåll och ersätta det med ett nytt feedbacksystem. Mer information finns i:Skicka och visa feedback för