Megosztás a következőn keresztül:


Rugalmas HTTP-alkalmazások létrehozása: Kulcsfejlesztési minták

Gyakori követelmény az átmeneti hibákból helyreállítható robusztus HTTP-alkalmazások létrehozása. Ez a cikk feltételezi, hogy már elolvasta a Bevezetés a rugalmas alkalmazásfejlesztésbe című cikket, mivel ez a cikk kiterjeszti az alapvető fogalmakat. A rugalmas HTTP-alkalmazások létrehozásához a Microsoft.Extensions.Http.Resilience NuGet csomag rugalmassági mechanizmusokat biztosít kifejezetten a HttpClient. Ez a NuGet-csomag a könyvtárra és a Microsoft.Extensions.Resilience Pollyra támaszkodik, amely egy népszerű nyílt forráskódú projekt. További információ: Polly.

Első lépések

A rugalmassági minták HTTP-alkalmazásokban való használatához telepítse a Microsoft.Extensions.Http.Resilience NuGet csomagot.

dotnet add package Microsoft.Extensions.Http.Resilience --version 8.0.0

További információ: dotnet add package or Manage package dependencies in .NET applications.

Rugalmasság hozzáadása HTTP-ügyfélhez

Ha rugalmasságot szeretne adni egy HttpClientadott metódushoz, a hívásláncot a IHttpClientBuilder rendelkezésre álló AddHttpClient metódusok hívásából visszaadott típushoz köti. További információ: IHttpClientFactory with .NET.

Számos rugalmasság-központú bővítmény érhető el. Némelyik standard, így különböző iparági ajánlott eljárásokat alkalmaz, míg mások testreszabhatóbbak. Rugalmasság hozzáadásakor csak egy rugalmasságkezelőt kell hozzáadnia, és kerülnie kell a kezelők halmozását. Ha több rugalmasságkezelőt kell hozzáadnia, fontolja meg a AddResilienceHandler bővítménymetódus használatát, amely lehetővé teszi a rugalmassági stratégiák testreszabását.

Fontos

A cikkben szereplő összes példa a AddHttpClient Microsoft.Extensions.Http kódtár API-jára támaszkodik, amely egy példányt IHttpClientBuilder ad vissza. A IHttpClientBuilder példány a rugalmasságkezelő konfigurálására HttpClient és hozzáadására szolgál.

Standard rugalmasságkezelő hozzáadása

A standard rugalmasságkezelő több, egymásra halmozott rugalmassági stratégiát használ, az alapértelmezett beállításokkal pedig elküldheti a kéréseket, és kezelheti az esetleges átmeneti hibákat. A rendszer hozzáadja a standard rugalmasságkezelőt, ha meghívja a AddStandardResilienceHandler bővítménymetódust egy IHttpClientBuilder példányon.

var services = new ServiceCollection();

var httpClientBuilder = services.AddHttpClient<ExampleClient>(
    configureClient: static client =>
    {
        client.BaseAddress = new("https://jsonplaceholder.typicode.com");
    });

A fenti kód a következőket végzi el:

  • Létrehoz egy példányt ServiceCollection .
  • Hozzáad egy HttpClient típust a ExampleClient szolgáltatástárolóhoz.
  • HttpClient Alapcímként való használatra konfigurálja a beállítást"https://jsonplaceholder.typicode.com".
  • Létrehozza a httpClientBuilder cikkben szereplő többi példában használtakat.

Egy valósabb példa az üzemeltetésre támaszkodna, például a .NET Generic Host cikkben leírtakra. A Microsoft.Extensions.Hosting NuGet csomag használatával vegye figyelembe a következő frissített példát:

using Http.Resilience.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

IHttpClientBuilder httpClientBuilder = builder.Services.AddHttpClient<ExampleClient>(
    configureClient: static client =>
    {
        client.BaseAddress = new("https://jsonplaceholder.typicode.com");
    });

Az előző kód hasonló a manuális ServiceCollection létrehozási megközelítéshez, de ehelyett egy Host.CreateApplicationBuilder() olyan gazdagép létrehozására támaszkodik, amely elérhetővé teszi a szolgáltatásokat.

A ExampleClient definíció a következő:

using System.Net.Http.Json;

namespace Http.Resilience.Example;

/// <summary>
/// An example client service, that relies on the <see cref="HttpClient"/> instance.
/// </summary>
/// <param name="client">The given <see cref="HttpClient"/> instance.</param>
internal sealed class ExampleClient(HttpClient client)
{
    /// <summary>
    /// Returns an <see cref="IAsyncEnumerable{T}"/> of <see cref="Comment"/>s.
    /// </summary>
    public IAsyncEnumerable<Comment?> GetCommentsAsync()
    {
        return client.GetFromJsonAsAsyncEnumerable<Comment>("/comments");
    }
}

A fenti kód a következőket végzi el:

  • Olyan típust ExampleClient definiál, amely konstruktort fogad el HttpClient.
  • Olyan metódust GetCommentsAsync tesz elérhetővé, amely get kérést küld a /comments végpontnak, és visszaadja a választ.

A Comment típus a következőképpen van definiálva:

namespace Http.Resilience.Example;

public record class Comment(
    int PostId, int Id, string Name, string Email, string Body);

Mivel létrehozott egy IHttpClientBuilder (httpClientBuilder) modellt, és most már ismeri a megvalósítást és a ExampleClient megfelelő Comment modellt, vegye figyelembe a következő példát:

httpClientBuilder.AddStandardResilienceHandler();

Az előző kód hozzáadja a standard rugalmasságkezelőt a HttpClient. A legtöbb rugalmassági API-hoz hasonlóan vannak túlterhelések, amelyek lehetővé teszik az alapértelmezett beállítások testreszabását és az alkalmazott rugalmassági stratégiákat.

Standard rugalmasságkezelő alapértelmezései

Az alapértelmezett konfiguráció öt rugalmassági stratégiát láncol a következő sorrendben (a legkülsőtől a legbelsőig):

Rendelés Stratégia Leírás Defaults
1 Sebességkorlátozó A sebességkorlátozó folyamat korlátozza a függőségnek küldött egyidejű kérések maximális számát. Sor: 0
Engedély: 1_000
2 Teljes időtúllépés A teljes kérelem-időtúllépési folyamat általános időtúllépést alkalmaz a végrehajtásra, biztosítva, hogy a kérés , beleértve az újrapróbálkozási kísérleteket is, ne lépje túl a konfigurált korlátot. Teljes időtúllépés: 30-as
3 Ismét Az újrapróbálkozási folyamat újrapróbálkozza a kérést, ha a függőség lassú, vagy átmeneti hibát ad vissza. Újrapróbálkozások maximális száma: 3
Visszalépés: Exponential
Jitter használata: true
Késleltetés:2s
4 Megszakító Az áramkör-megszakító blokkolja a végrehajtást, ha túl sok közvetlen hibát vagy időtúllépést észlel. Hibaarány: 10%
Minimális átviteli sebesség: 100
Mintavétel időtartama: 30-as
Szünet időtartama: 5s
5 Kísérlet időtúllépése A kísérlet időtúllépési folyamata korlátozza az egyes kérelmek kísérleteinek időtartamát, és ha túllépi, eldobja azt. Kísérlet időtúllépése: 10s

Újrapróbálkozás és megszakítók

Az újrapróbálkozás és az áramkör-megszakító stratégiák egyszerre kezelik az adott HTTP-állapotkódok és -kivételek készletét. Vegye figyelembe a következő HTTP-állapotkódokat:

  • HTTP 500 vagy újabb (kiszolgálói hibák)
  • HTTP 408 (Kérelem időtúllépése)
  • HTTP 429 (Túl sok kérés)

Emellett ezek a stratégiák a következő kivételeket kezelik:

  • HttpRequestException
  • TimeoutRejectedException

A HTTP-metódusok adott listájának újrapróbálkozásainak letiltása

Alapértelmezés szerint a standard rugalmasságkezelő úgy van konfigurálva, hogy újrapróbálkozjon az összes HTTP-metódushoz. Egyes alkalmazások esetében az ilyen viselkedés nemkívánatos vagy akár káros is lehet. Ha például egy POST-kérelem új rekordot szúr be egy adatbázisba, akkor az ilyen kérés újrapróbálkozása adatkettőződéshez vezethet. Ha le kell tiltania az újrapróbálkozásokat a HTTP-metódusok adott listájához, használhatja a DisableFor(HttpRetryStrategyOptions, HttpMethod[]) metódust:

httpClientBuilder.AddStandardResilienceHandler(options =>
{
    options.Retry.DisableFor(HttpMethod.Post, HttpMethod.Delete);
});

Másik lehetőségként használhatja a DisableForUnsafeHttpMethods(HttpRetryStrategyOptions) metódust is, amely letiltja a POST, PATCH, PUT, DELETEés CONNECT kérések újrapróbálkozását. A RFCszerint ezek a módszerek nem tekinthetők biztonságosnak; ami azt jelenti, hogy szemantikájuk nem írásvédett:

httpClientBuilder.AddStandardResilienceHandler(options =>
{
    options.Retry.DisableForUnsafeHttpMethods();
});

Standard fedezetkezelő hozzáadása

A standard fedezetkezelő szabványos fedezetkezelő mechanizmussal burkolja a kérés végrehajtását. A hedging párhuzamosan újrapróbálkozza a lassú kéréseket.

A standard fedezetkezelő használatához hívja meg AddStandardHedgingHandler a bővítménymetódust. Az alábbi példa a ExampleClient standard fedezetkezelő használatára konfigurálja.

httpClientBuilder.AddStandardHedgingHandler();

Az előző kód hozzáadja a standard fedezetkezelőt a HttpClient.

A standard fedezetkezelő alapértelmezései

A standard fedezeti szolgáltatás megszakítók készletét használja annak biztosítására, hogy a nem megfelelő végpontok ne legyenek védve. Alapértelmezés szerint a készlet kiválasztása az URL-szolgáltatón (séma + gazdagép + port) alapul.

Tipp.

Javasoljuk, hogy hívással StandardHedgingHandlerBuilderExtensions.SelectPipelineByAuthority vagy StandardHedgingHandlerBuilderExtensions.SelectPipelineBy speciálisabb forgatókönyvek esetén konfigurálja a stratégiák kiválasztásának módját.

Az előző kód hozzáadja a standard fedezetkezelőt a IHttpClientBuilder. Az alapértelmezett konfiguráció öt rugalmassági stratégiát láncol a következő sorrendben (a legkülsőtől a legbelsőig):

Rendelés Stratégia Leírás Defaults
1 Kérelem teljes időtúllépése A teljes kérelem-időtúllépési folyamat a végrehajtás teljes időtúllépését alkalmazza, biztosítva, hogy a kérelem , beleértve a fedezeti kísérleteket is, ne lépje túl a konfigurált korlátot. Teljes időtúllépés: 30-as
2 Fedezeti A fedezeti stratégia több végponton hajtja végre a kéréseket abban az esetben, ha a függőség lassú vagy átmeneti hibát ad vissza. Az útválasztás beállítások, alapértelmezés szerint csak az eredeti HttpRequestMessageURL-cím fedezetét adja meg. Minimális kísérletek: 1
Maximális kísérletek: 10
Késés: 2s
3 Sebességkorlátozó (végpontonként) A sebességkorlátozó folyamat korlátozza a függőségnek küldött egyidejű kérések maximális számát. Sor: 0
Engedély: 1_000
4 Megszakító (végpontonként) Az áramkör-megszakító blokkolja a végrehajtást, ha túl sok közvetlen hibát vagy időtúllépést észlel. Hibaarány: 10%
Minimális átviteli sebesség: 100
Mintavétel időtartama: 30-as
Szünet időtartama: 5s
5 Kísérlet időtúllépése (végpontonként) A kísérlet időtúllépési folyamata korlátozza az egyes kérelmek kísérleteinek időtartamát, és ha túllépi, eldobja azt. Időtúllépés: 10s

Hedging handler route selection testreszabása

A standard fedezetkezelő használatakor testre szabhatja a kérésvégpontok kiválasztásának módját, ha különböző bővítményeket hív meg a IRoutingStrategyBuilder típuson. Ez olyan helyzetekben lehet hasznos, mint az A/B-tesztelés, ahol a kérések egy százalékát egy másik végpontra szeretné irányítani:

httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
    // Hedging allows sending multiple concurrent requests
    builder.ConfigureOrderedGroups(static options =>
    {
        options.Groups.Add(new UriEndpointGroup()
        {
            Endpoints =
            {
                // Imagine a scenario where 3% of the requests are 
                // sent to the experimental endpoint.
                new() { Uri = new("https://example.net/api/experimental"), Weight = 3 },
                new() { Uri = new("https://example.net/api/stable"), Weight = 97 }
            }
        });
    });
});

A fenti kód a következőket végzi el:

  • Hozzáadja a hedging kezelőt a IHttpClientBuilder.
  • Konfigurálja a IRoutingStrategyBuilder metódust a ConfigureOrderedGroups rendezett csoportok konfigurálásához.
  • Hozzáad egy olyan EndpointGroup fájlt orderedGroup , amely a kérések 3%-át a https://example.net/api/experimental végpontra irányítja, a kérések 97%-át pedig a https://example.net/api/stable végpontra.
  • IRoutingStrategyBuilder A metódus használatával konfigurálja a ConfigureWeightedGroups

Súlyozott csoport konfigurálásához hívja meg a ConfigureWeightedGroups metódust a IRoutingStrategyBuilder típuson. Az alábbi példa a IRoutingStrategyBuilder súlyozott csoportok konfigurálásához konfigurálja a ConfigureWeightedGroups metódust.

httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
    // Hedging allows sending multiple concurrent requests
    builder.ConfigureWeightedGroups(static options =>
    {
        options.SelectionMode = WeightedGroupSelectionMode.EveryAttempt;

        options.Groups.Add(new WeightedUriEndpointGroup()
        {
            Endpoints =
            {
                // Imagine A/B testing
                new() { Uri = new("https://example.net/api/a"), Weight = 33 },
                new() { Uri = new("https://example.net/api/b"), Weight = 33 },
                new() { Uri = new("https://example.net/api/c"), Weight = 33 }
            }
        });
    });
});

A fenti kód a következőket végzi el:

  • Hozzáadja a hedging kezelőt a IHttpClientBuilder.
  • Konfigurálja a IRoutingStrategyBuilder módszert a ConfigureWeightedGroups súlyozott csoportok konfigurálásához.
  • A következőre állítja a SelectionMode következőt WeightedGroupSelectionMode.EveryAttempt: .
  • Hozzáadja WeightedEndpointGroup a weightedGroup végponthoz a kérések 33%-át, a https://example.net/api/a kérések 33%-át a https://example.net/api/b végponthoz, a kérések 33%-át pedig a https://example.net/api/c végponthoz.

Tipp.

A fedezeti kísérletek maximális száma közvetlenül korrelál a konfigurált csoportok számával. Ha például két csoportja van, a kísérletek maximális száma kettő.

További információ: Polly docs: Hedging resilience strategy.

Gyakori, hogy rendezett vagy súlyozott csoportot konfigurál, de mindkettő konfigurálható. A rendezett és súlyozott csoportok használata olyan helyzetekben hasznos, amikor a kérések egy százalékát egy másik végpontra szeretné elküldeni, ilyen például az A/B-tesztelés.

Egyéni rugalmasságkezelők hozzáadása

A nagyobb vezérlés érdekében testre szabhatja a rugalmasságkezelőket az AddResilienceHandler API használatával. Ez a metódus elfogad egy meghatalmazottat, aki konfigurálja a ResiliencePipelineBuilder<HttpResponseMessage> rugalmassági stratégiák létrehozásához használt példányt.

Egy elnevezett rugalmasságkezelő konfigurálásához hívja meg a AddResilienceHandler bővítménymetódust a kezelő nevével. Az alábbi példa egy nevesített, úgynevezett rugalmasságkezelőt "CustomPipeline"konfigurál.

httpClientBuilder.AddResilienceHandler(
    "CustomPipeline",
    static builder =>
{
    // See: https://www.pollydocs.org/strategies/retry.html
    builder.AddRetry(new HttpRetryStrategyOptions
    {
        // Customize and configure the retry logic.
        BackoffType = DelayBackoffType.Exponential,
        MaxRetryAttempts = 5,
        UseJitter = true
    });

    // See: https://www.pollydocs.org/strategies/circuit-breaker.html
    builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
    {
        // Customize and configure the circuit breaker logic.
        SamplingDuration = TimeSpan.FromSeconds(10),
        FailureRatio = 0.2,
        MinimumThroughput = 3,
        ShouldHandle = static args =>
        {
            return ValueTask.FromResult(args is
            {
                Outcome.Result.StatusCode:
                    HttpStatusCode.RequestTimeout or
                        HttpStatusCode.TooManyRequests
            });
        }
    });

    // See: https://www.pollydocs.org/strategies/timeout.html
    builder.AddTimeout(TimeSpan.FromSeconds(5));
});

A fenti kód a következőket végzi el:

  • Hozzáad egy rugalmasságkezelőt a szolgáltatástárolóhoz a névvel "CustomPipeline"pipelineName .
  • Exponenciális visszalépéssel, öt újrapróbálkozással és jitter-beállítással rendelkező újrapróbálkozási stratégiát ad hozzá a rugalmasság-készítőhöz.
  • 10 másodperces mintavételezési időtartammal, 0,2 (20%) hibaaránysal, legalább három átviteli sebességgel, valamint egy olyan predikátummal bővíti az áramkör-megszakító stratégiát, amely kezeli és RequestTimeout HTTP-állapotkódokat TooManyRequests a rugalmasság-készítőhöz.
  • Öt másodperces időtúllépési stratégiát ad hozzá a rugalmasság-készítőhöz.

Az egyes rugalmassági stratégiákhoz számos lehetőség áll rendelkezésre. További információ: Polly docs: Strategies. További információ a meghatalmazottak konfigurálásáról ShouldHandle : Polly-dokumentumok: Hibakezelés reaktív stratégiákban.

Dinamikus újratöltés

A Polly támogatja a konfigurált rugalmassági stratégiák dinamikus újratöltését. Ez azt jelenti, hogy futásidőben módosíthatja a rugalmassági stratégiák konfigurációját. A dinamikus újratöltés engedélyezéséhez használja a megfelelő AddResilienceHandler túlterhelést, amely elérhetővé teszi a ResilienceHandlerContext. A kontextust figyelembe véve hívja meg EnableReloads a megfelelő rugalmassági stratégia lehetőségeit:

httpClientBuilder.AddResilienceHandler(
    "AdvancedPipeline",
    static (ResiliencePipelineBuilder<HttpResponseMessage> builder,
        ResilienceHandlerContext context) =>
    {
        // Enable reloads whenever the named options change
        context.EnableReloads<HttpRetryStrategyOptions>("RetryOptions");

        // Retrieve the named options
        var retryOptions =
            context.GetOptions<HttpRetryStrategyOptions>("RetryOptions");

        // Add retries using the resolved options
        builder.AddRetry(retryOptions);
    });

A fenti kód a következőket végzi el:

  • Hozzáad egy rugalmasságkezelőt a szolgáltatástárolóhoz a névvel "AdvancedPipeline"pipelineName .
  • Engedélyezi a folyamat újratöltését, "AdvancedPipeline" amikor a névvel ellátott RetryStrategyOptions beállítások megváltoznak.
  • Lekéri az elnevezett beállításokat a IOptionsMonitor<TOptions> szolgáltatásból.
  • Újrapróbálkozési stratégiát ad hozzá a lekért beállításokkal a rugalmasság-szerkesztőhöz.

További információ: Polly-dokumentumok: Speciális függőséginjektálás.

Ez a példa egy olyan beállítási szakaszra támaszkodik, amely módosítható, például egy appsettings.json fájlra. Vegye figyelembe a következő appsettings.json fájlt:

{
    "RetryOptions": {
        "Retry": {
            "BackoffType": "Linear",
            "UseJitter": false,
            "MaxRetryAttempts": 7
        }
    }
}

Képzelje el, hogy ezek a beállítások az alkalmazás konfigurációjához voltak kötve, és a HttpRetryStrategyOptions következő szakaszhoz kötötték:"RetryOptions"

var section = builder.Configuration.GetSection("RetryOptions");

builder.Services.Configure<HttpStandardResilienceOptions>(section);

További információ: Beállítások minta a .NET-ben.

Példa használatra

Az alkalmazás függőséginjektálásratámaszkodik a probléma és annak ExampleClient megfelelő HttpClientfeloldása érdekében. A kód létrehozza a IServiceProvider kódot, és feloldja azt ExampleClient .

IHost host = builder.Build();

ExampleClient client = host.Services.GetRequiredService<ExampleClient>();

await foreach (Comment? comment in client.GetCommentsAsync())
{
    Console.WriteLine(comment);
}

A fenti kód a következőket végzi el:

  • A következőt IServiceProvider építi ki: .ServiceCollection>
  • A feloldja a ExampleClient következőt: .IServiceProvider>
  • Meghívja a GetCommentsAsync metódust a ExampleClient megjegyzések lekéréséhez.
  • Minden megjegyzést a konzolra ír.

Képzeljen el egy olyan helyzetet, amikor a hálózat leáll, vagy a kiszolgáló nem válaszol. Az alábbi ábra bemutatja, hogy a rugalmassági stratégiák hogyan kezelik a helyzetet a módszer és ExampleClient a GetCommentsAsync módszer alapján:

Példa HTTP GET munkafolyamat rugalmassági folyamattal.

Az előző diagram a következőt ábrázolja:

  • A ExampleClient http GET kérést küld a /comments végpontnak.
  • A HttpResponseMessage kiértékelés eredménye:
    • Ha a válasz sikeres (HTTP 200), a rendszer visszaadja a választ.
    • Ha a válasz sikertelen (HTTP nem 200), a rugalmassági folyamat a konfigurált rugalmassági stratégiákat alkalmazza.

Bár ez egy egyszerű példa, bemutatja, hogyan használhatók a rugalmassági stratégiák az átmeneti hibák kezelésére. További információ: Polly docs: Strategies.

Ismert problémák

A következő szakaszok részletesen ismertetik a különböző ismert problémákat.

Kompatibilitás a Grpc.Net.ClientFactory csomaggal

Ha verziót vagy korábbi verziót Grpc.Net.ClientFactory használ2.63.0, akkor a gRPC-ügyfél szabványos rugalmassági vagy fedezetkezelőinek engedélyezése futásidejű kivételt okozhat. Vegye figyelembe a következő kódmintát:

services
    .AddGrpcClient<Greeter.GreeterClient>()
    .AddStandardResilienceHandler();

Az előző kód a következő kivételt eredményezi:

System.InvalidOperationException: The ConfigureHttpClient method is not supported when creating gRPC clients. Unable to create client with name 'GreeterClient'.

A probléma megoldásához javasoljuk, hogy frissítsen verzióra vagy újabb verzióra Grpc.Net.ClientFactory2.64.0 .

Van egy összeállítási idő ellenőrzése, amely ellenőrzi, hogy verziót vagy korábbi verziót Grpc.Net.ClientFactory használ-e2.63.0, és ha ön az ellenőrzés, fordítási figyelmeztetést hoz létre. A figyelmeztetést a következő tulajdonság beállításával tilthatja le a projektfájlban:

<PropertyGroup>
  <SuppressCheckGrpcNetClientFactoryVersion>true</SuppressCheckGrpcNetClientFactoryVersion>
</PropertyGroup>

Kompatibilitás a .NET Application Insights szolgáltatással

Ha .NET Application Insightst használ, akkor a rugalmassági funkciók alkalmazásbeli engedélyezésével az Összes Application Insights-telemetria hiányzik. A probléma akkor fordul elő, ha a rugalmassági funkciók regisztrálva lesznek az Application Insights-szolgáltatások előtt. Fontolja meg a következő mintát, amely a problémát okozza:

// At first, we register resilience functionality.
services.AddHttpClient().AddStandardResilienceHandler();

// And then we register Application Insights. As a result, Application Insights doesn't work.
services.AddApplicationInsightsTelemetry();

A problémát az Application Insights következő hibája okozza, és az Application Insights-szolgáltatások rugalmassági funkciók előtt történő regisztrálásával javítható, ahogyan az alább látható:

// We register Application Insights first, and now it will be working correctly.
services.AddApplicationInsightsTelemetry();
services.AddHttpClient().AddStandardResilienceHandler();