Rugalmas HTTP-kérések implementálása az IHttpClientFactory használatával

Tipp.

Ez a tartalom egy részlet a .NET-alkalmazásokhoz készült .NET-alkalmazásokhoz készült eBook, .NET Microservices Architecture című eBookból, amely elérhető a .NET Docs-on vagy egy ingyenesen letölthető PDF-fájlként, amely offline módban is olvasható.

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

IHttpClientFactory a .NET Core 2.1 óta elérhető , véleményezett gyár által DefaultHttpClientFactorymegvalósított szerződés, amely az alkalmazásokban használandó példányok létrehozására HttpClient szolgál.

A .NET-ben elérhető eredeti HttpClient-osztálysal kapcsolatos problémák

Az eredeti és jól ismert HttpClient osztály könnyen használható, de bizonyos esetekben sok fejlesztő nem használja megfelelően.

Bár ez az osztály implementálja IDisposable, deklarálja és példányosítja azt egy using utasításon belül, nem ajánlott, mert amikor az HttpClient objektumot megsemmisítik, a mögöttes szoftvercsatorna nem szabadul fel azonnal, ami szoftvercsatornák kimerüléséhez vezethet. A problémáról további információt a HttpClient helytelen használata és a szoftver destabilizálása című blogbejegyzésben talál.

Ezért azt tervezik, HttpClient hogy egyszer példányosítják, és az alkalmazás teljes élettartama alatt újra felhasználják. Az osztály minden kéréshez való példányosítása HttpClient kimeríti a nagy terhelés alatt elérhető foglalatok számát. Ez a probléma hibákat fog eredményezni SocketException . A probléma megoldásának lehetséges módjai az HttpClient objektum önálló vagy statikus létrehozásán alapulnak, amint azt a HttpClient-használatról szóló Microsoft-cikk ismerteti. Ez jó megoldás lehet olyan rövid élettartamú konzolalkalmazásokhoz vagy hasonlókhoz, amelyek naponta néhányszor futnak.

Egy másik probléma, amelybe a fejlesztők belefutnak, a hosszú ideig futó folyamatok megosztott példányának HttpClient használata. Ha a HttpClient példányosítása egyetlen vagy statikus objektumként történik, nem kezeli a dotnet/runtime GitHub-adattár jelen problémájában leírt DNS-módosításokat.

A probléma azonban nem önmagában HttpClient van, hanem a HttpClient alapértelmezett konstruktorával, mivel egy új konkrét példányt hoz létre, amely a szoftvercsatornák kimerülésével HttpMessageHandler és a DNS-módosításokkal kapcsolatos fent említett problémákat tartalmazza.

A fent említett problémák megoldása és a példányok kezelhetővé tétele HttpClient érdekében a .NET Core 2.1 két megközelítést vezetett be, amelyek közül az egyik a IHttpClientFactory. Ez egy felület, amellyel konfigurálhatók és hozhatók létre HttpClient példányok egy alkalmazásban a Dependency Injection (DI) használatával. Emellett a Polly-alapú köztes szoftver bővítményeit is biztosítja, hogy kihasználhassa a HttpClient kezelőinek delegálását.

A másik lehetőség a SocketsHttpHandler konfigurált PooledConnectionLifetime. Ez a megközelítés hosszú élettartamú vagy static egypéldányos HttpClient példányokra vonatkozik. A különböző stratégiákról további információt a .NET httpClient-irányelvei című témakörben talál.

A Polly egy átmeneti hibakezelési kódtár, amely segít a fejlesztőknek rugalmasságot adni az alkalmazásaikhoz bizonyos előre meghatározott szabályzatok folyékony és szálbiztos használatával.

Az IHttpClientFactory használatának előnyei

A jelenlegi implementáció a IHttpClientFactorykövetkező előnyöket nyújtja IHttpMessageHandlerFactory:

  • Központi helyet biztosít a logikai HttpClient objektumok elnevezéséhez és konfigurálásához. Konfigurálhat például egy olyan ügyfelet (szolgáltatásügynököt), amely előre konfigurálva van egy adott mikroszolgáltatás eléréséhez.
  • Kodifikálja a kimenő köztes szoftver fogalmát a Polly-alapú köztes szoftver kezelőinek HttpClient delegálásával és implementálásával, hogy kihasználhassa a Polly rugalmasságra vonatkozó szabályzatait.
  • HttpClient már rendelkezik a kimenő HTTP-kérésekhez összekapcsolható kezelők delegálásának fogalmával. HTTP-ügyfeleket regisztrálhat a gyárban, és Polly-kezelővel Polly-szabályzatokat használhat az Újrapróbálkozáshoz, a CircuitBreakershez és így tovább.
  • Kezelheti az élettartamot HttpMessageHandler , hogy elkerülje azokat a problémákat/problémákat, amelyek az élettartamok kezelésekor HttpClient fordulhatnak elő.

Tipp.

A HttpClient DI által injektált példányok biztonságosan megsemmisíthetők, mivel a társított HttpMessageHandler példányokat a gyár kezeli. Az injektált HttpClient példányok a DI szempontjából átmenetiek, míg HttpMessageHandler a példányok hatókörrel rendelkezőnek tekinthetők. HttpMessageHandler a példányok saját DI-hatókörrel rendelkeznek, az alkalmazás hatókörétől ( például ASP.NET bejövő kérelem hatóköreitől). További információ: HttpClientFactory használata a .NET-ben.

Feljegyzés

A () megvalósítása IHttpClientFactory szorosan kapcsolódik a NuGet-csomag DI-implementációjáhozMicrosoft.Extensions.DependencyInjection.DefaultHttpClientFactory Ha di nélkül vagy más DI-implementációkkal kell használnia HttpClient , fontolja meg egy vagy egyetlentonn staticHttpClient használatát a beállítással PooledConnectionLifetime . További információ: HttpClient irányelvek a .NET-hez.

Az IHttpClientFactory használatának több módja

Az alkalmazásban többféleképpen is használhatja IHttpClientFactory :

  • Alapszintű használat
  • Névvel ellátott ügyfelek használata
  • Beírt ügyfelek használata
  • Generált ügyfelek használata

A rövidség kedvéért ez az útmutató bemutatja a leginkább strukturált felhasználási IHttpClientFactorymódot, azaz a típusos ügyfelek (szolgáltatásügynök-minta) használatát. Az összes lehetőség azonban dokumentálva van, és jelenleg a használatról szóló IHttpClientFactory cikkben szerepel.

Feljegyzés

Ha az alkalmazás cookie-kat igényel, érdemes lehet elkerülni a használatot IHttpClientFactory az alkalmazásban. Az ügyfelek kezelésének alternatív módjaiért tekintse meg a HTTP-ügyfelek használatának irányelveit

Gépelt ügyfelek használata az IHttpClientFactory használatával

Szóval, mi az a "Gépelt ügyfél"? Ez csak egy HttpClient előre konfigurált bizonyos használatra. Ez a konfiguráció bizonyos értékeket tartalmazhat, például az alapkiszolgálót, a HTTP-fejléceket vagy az időtúllépéseket.

Az alábbi ábra bemutatja, hogyan használják a gépelt ügyfeleket IHttpClientFactory:

Diagram showing how typed clients are used with IHttpClientFactory.

8-4. ábra. Használat IHttpClientFactory gépelt ügyfélosztályokkal.

A fenti képen egy ClientService (vezérlő vagy ügyfélkód által használt) a regisztrált IHttpClientFactoryfelhasználó által létrehozott kódot használjaHttpClient. Ez a gyár hozzárendel egy HttpMessageHandler készletet a HttpClient. A HttpClient Lekérdezési szabályzatokkal konfigurálható, amikor a DI-tárolóban regisztrálja a IHttpClientFactory bővítménymetódussal AddHttpClient.

A fenti struktúra konfigurálásához adja hozzá IHttpClientFactory az alkalmazáshoz a Microsoft.Extensions.Http bővítménymetódust IServiceCollectiontartalmazó AddHttpClient NuGet-csomagot. Ez a bővítménymetódus regisztrálja a belső DefaultHttpClientFactory osztályt, amelyet egyetlentonként kell használni az interfészhez IHttpClientFactory. Átmeneti konfigurációt határoz meg a HttpMessageHandlerBuilder. A készletből származó üzenetkezelőt (HttpMessageHandler objektumot) a HttpClient gyári visszaadott használja.

A következő kódrészletben láthatja, hogyan AddHttpClient() regisztrálhatók a használni HttpClientkívánt típusú ügyfelek (szolgáltatásügynökök).

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

Az ügyfélszolgáltatások regisztrálása az előző kódrészletben látható módon minden szolgáltatáshoz szabványossá HttpClient teszi a DefaultClientFactory létrehozást. A beírt ügyfél átmenetiként van regisztrálva a DI-tárolóban. Az előző kódban AddHttpClient() a CatalogService, a BasketService, az OrderingService szolgáltatást átmeneti szolgáltatásként regisztrálja, hogy közvetlenül, további regisztrációk nélkül lehessen injektálni és felhasználni őket.

A regisztrációban a példányspecifikus konfigurációt is hozzáadhatja például az alapcím konfigurálásához, és hozzáadhat néhány rugalmassági szabályzatot, ahogy az alábbi ábrán látható:

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

Ebben a következő példában a fenti szabályzatok egyikének konfigurációját láthatja:

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

A Polly használatával kapcsolatos további részleteket a Következő cikkben talál.

HttpClient-élettartamok

Minden alkalommal, amikor egy objektumot HttpClient kap a IHttpClientFactoryrendszer, egy új példányt ad vissza. De mindegyik HttpClient egy HttpMessageHandler készletezett és újrahasznált erőforrás-felhasználást IHttpClientFactory használ az erőforrás-felhasználás csökkentése érdekében, feltéve, hogy az HttpMessageHandlerélettartam még nem járt le.

A kezelők készletezése kívánatos, mivel az egyes kezelők általában saját mögöttes HTTP-kapcsolatokat kezelnek; a szükségesnél több kezelő létrehozása kapcsolati késéseket eredményezhet. Egyes kezelők korlátlan ideig nyitva tartják a kapcsolatokat, ami megakadályozhatja, hogy a kezelő reagáljon a DNS-változásokra.

A HttpMessageHandler készletben lévő objektumok élettartama az a időtartam, amely alatt a készlet egy HttpMessageHandler példánya újra felhasználható. Az alapértelmezett érték két perc, de felül lehet bírálni gépelt ügyfélenként. A felülbíráláshoz hívja meg SetHandlerLifetime() az IHttpClientBuilder ügyfél létrehozásakor visszaadott hívást az alábbi kódban látható módon:

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

Minden gépelt ügyfél saját konfigurált kezelő élettartam-értékkel rendelkezhet. Állítsa be az élettartamot a InfiniteTimeSpan kezelő lejáratának letiltásához.

Az injektált és konfigurált HttpClientet használó gépelt ügyfélosztályok implementálása

Előző lépésként definiálnia kell a beírt ügyfélosztályokat, például a mintakódban szereplő osztályokat, például a "BasketService", a "CatalogService", az "OrderingService" stb. osztályt. A gépelt ügyfél egy olyan osztály, amely elfogad egy HttpClient objektumot (a konstruktoron keresztül injektálva), és arra használja, hogy meghívjon egy távoli HTTP-szolgáltatást. Példa:

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

A gépelt ügyfelet (CatalogService a példában) a DI (Dependency Injection) aktiválja, ami azt jelenti, hogy a konstruktorban bármilyen regisztrált szolgáltatást elfogadhat a konstruktoron kívül HttpClient.

A gépelt ügyfél tulajdonképpen átmeneti objektum, ami azt jelenti, hogy minden alkalommal létrejön egy új példány, amikor szükség van rá. Minden egyes létrehozásakor új HttpClient példányt kap. A HttpMessageHandler készletben lévő objektumok azonban azok az objektumok, amelyeket több HttpClient példány is újra felhasznál.

A gépelt ügyfélosztályok használata

Végül, miután implementálta a gépelt osztályokat, regisztrálhatja és konfigurálhatja AddHttpClient()őket. Ezt követően bárhol használhatja őket, ahol a DI szolgáltatásokat injektálja, például a Razor oldalkódjában vagy egy MVC webalkalmazás-vezérlőben, amely az alábbi eShopOnContainers-kódban látható:

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
        }

        }
}

Eddig a pontig a fenti kódrészlet csak a szokásos HTTP-kérések végrehajtására szolgáló példát mutatja. A "varázslat" azonban a következő szakaszokban jelenik meg, ahol bemutatja, hogy az összes HTTP-kérés rugalmas HttpClient szabályzatokkal rendelkezhet, például exponenciális visszakapcsolással végzett újrapróbálkozások, megszakítók, hitelesítési jogkivonatokat használó biztonsági funkciók vagy akár bármilyen más egyéni funkció. Mindez pedig csak úgy végezhető el, ha szabályzatokat ad hozzá, és kezelőket delegál a regisztrált gépelt ügyfelekhez.

További erőforrások