Sdílet prostřednictvím


Vytváření odolných aplikací HTTP: Klíčové vývojové vzory

Vytváření robustních aplikací HTTP, které se můžou zotavit z přechodných chyb, je běžným požadavkem. Tento článek předpokládá, že už jste si přečetli úvod k odolnému vývoji aplikací, protože tento článek rozšiřuje základní koncepty. Balíček NuGet Microsoft.Extensions.Http.Resilience pomáhá vytvářet odolné aplikace HTTP a poskytuje mechanismy odolnosti určené speciálně pro .HttpClient Tento balíček NuGet spoléhá na knihovnu Microsoft.Extensions.Resilience a Polly, což je oblíbený opensourcový projekt. Další informace najdete v tématu Polly.

Začínáme

Pokud chcete používat vzorce odolnosti v aplikacích HTTP, nainstalujte balíček NuGet Microsoft.Extensions.Http.Resilience .

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

Další informace naleznete v tématu dotnet add package or Manage package dependencies in .NET applications.

Přidání odolnosti klienta HTTP

Chcete-li přidat odolnost proti chybám HttpClient, zřetězte volání typu IHttpClientBuilder , který je vrácen z volání některé z dostupných AddHttpClient metod. Další informace najdete v tématu IHttpClientFactory s .NET.

K dispozici je několik rozšíření orientovaných na odolnost. Některé jsou standardní, a proto využívají různé osvědčené postupy v odvětví a jiné jsou lépe přizpůsobitelné. Při přidávání odolnosti byste měli přidat pouze jednu obslužnou rutinu odolnosti a vyhnout se zásobníkovacím obslužným rutinám. Pokud potřebujete přidat více obslužných rutin odolnosti, měli byste zvážit použití AddResilienceHandler metody rozšíření, která umožňuje přizpůsobit strategie odolnosti.

Důležité

Všechny příklady v tomto článku využívají AddHttpClient rozhraní API z knihovny Microsoft.Extensions.Http , která vrací IHttpClientBuilder instanci. Instance IHttpClientBuilder se používá ke konfiguraci HttpClient a přidání obslužné rutiny odolnosti.

Přidání standardní obslužné rutiny odolnosti

Standardní obslužná rutina odolnosti používá několik strategií odolnosti skládaných na sebe navzájem s výchozími možnostmi odesílání požadavků a zpracování přechodných chyb. Standardní obslužná rutina odolnosti je přidána voláním AddStandardResilienceHandler metody rozšíření v IHttpClientBuilder instanci.

var services = new ServiceCollection();

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

Předchozí kód:

  • ServiceCollection Vytvoří instanci.
  • HttpClient Přidá typ pro ExampleClient kontejner služby.
  • Nakonfiguruje HttpClient , aby se používala "https://jsonplaceholder.typicode.com" jako základní adresa.
  • httpClientBuilder Vytvoří, která se použije v dalších příkladech v tomto článku.

Real-world example would rely on hosting, such as that described in the .NET Generic Host article. Při použití balíčku NuGet Microsoft.Extensions.Hosting zvažte následující aktualizovaný příklad:

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

Předchozí kód se podobá přístupu ručního ServiceCollection vytváření, ale místo toho spoléhá na Host.CreateApplicationBuilder() sestavení hostitele, který zveřejňuje služby.

Definuje se ExampleClient takto:

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

Předchozí kód:

  • Definuje ExampleClient typ, který má konstruktor, který přijímá .HttpClient
  • Zveřejňuje metodu GetCommentsAsync , která odešle požadavek GET do koncového /comments bodu a vrátí odpověď.

Typ Comment je definován takto:

namespace Http.Resilience.Example;

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

Vzhledem k tomu, že jste vytvořili IHttpClientBuilder (httpClientBuilder) a teď rozumíte ExampleClient implementaci a odpovídajícímu Comment modelu, zvažte následující příklad:

httpClientBuilder.AddStandardResilienceHandler();

Předchozí kód přidá do souboru standardní obslužnou rutinu HttpClientodolnosti . Podobně jako u většiny rozhraní API odolnosti existují přetížení, která umožňují přizpůsobit výchozí možnosti a použít strategie odolnosti.

Výchozí hodnoty obslužné rutiny standardní odolnosti

Výchozí konfigurace zřetězí pět strategií odolnosti v následujícím pořadí (od nejkrajnějšího po nejvnitřnější):

Objednávka Strategie Popis Defaults
1 Omezovač rychlosti Kanál omezovače rychlosti omezuje maximální počet souběžných požadavků odesílaných do závislosti. Fronta: 0
Dovolit: 1_000
2 Celkový časový limit Kanál časového limitu celkového časového limitu požadavku použije na spuštění celkový časový limit, který zajistí, že požadavek, včetně pokusů o opakování, nepřekročí nakonfigurovaný limit. Celkový časový limit: 30s
3 Zkusit znovu Kanál opakování opakuje požadavek v případě, že je závislost pomalá nebo vrací přechodnou chybu. Maximální počet opakování: 3
Zpět: Exponential
Použití zatřesku: true
Zpoždění:2s
4 Jistič Jistič blokuje provádění, pokud se zjistí příliš mnoho přímých selhání nebo časových limitů. Poměr selhání: 10 %
Minimální propustnost: 100
Doba trvání vzorkování: 30s
Doba trvání přestávky: 5s
5 Časový limit pokusu Kanál časového limitu pokusu omezuje každou dobu trvání pokusu o požadavek a vyvolá se, pokud je překročen. Časový limit pokusu: 10s

Opakování a jističe

Strategie opakování a jističe zpracovávají sadu konkrétních stavových kódů HTTP a výjimek. Zvažte následující stavové kódy HTTP:

  • HTTP 500 a vyšší (chyby serveru)
  • HTTP 408 (vypršení časového limitu požadavku)
  • HTTP 429 (Příliš mnoho požadavků)

Kromě toho tyto strategie zpracovávají následující výjimky:

  • HttpRequestException
  • TimeoutRejectedException

Přidání standardní obslužné rutiny hedgingu

Standardní obslužná rutina hedgingu zabalí provádění požadavku standardním mechanismem hedgingu. Souběžné opakování opakovaných pokusů o pomalé požadavky

Chcete-li použít standardní obslužnou rutinu hedgingu, volejte AddStandardHedgingHandler metodu rozšíření. Následující příklad nakonfiguruje ExampleClient použití standardní obslužné rutiny hedgingu.

httpClientBuilder.AddStandardHedgingHandler();

Předchozí kód přidá do něj standardní obslužnou rutinu hedgingu HttpClient.

Výchozí hodnoty standardní obslužné rutiny hedgingu

Standardní hedgování používá fond jističů, aby se zajistilo, že koncové body nejsou v pořádku zajištěné. Ve výchozím nastavení je výběr z fondu založený na autoritě adresy URL (schéma + hostitel + port).

Tip

Doporučujeme nakonfigurovat způsob, jakým jsou strategie vybrány voláním StandardHedgingHandlerBuilderExtensions.SelectPipelineByAuthority nebo StandardHedgingHandlerBuilderExtensions.SelectPipelineBy pro pokročilejší scénáře.

Předchozí kód přidá do něj standardní obslužnou rutinu hedgingu IHttpClientBuilder. Výchozí konfigurace zřetězí pět strategií odolnosti v následujícím pořadí (od nejkrajnějšího po nejvnitřnější):

Objednávka Strategie Popis Defaults
1 Celkový časový limit požadavku Kanál časového limitu celkového časového limitu požadavku použije na spuštění celkový časový limit, který zajistí, že požadavek, včetně pokusů o provedení, nepřekročí nakonfigurovaný limit. Celkový časový limit: 30s
2 Uhýbání Strategie zřizování provádí požadavky na více koncových bodů v případě, že je závislost pomalá nebo vrací přechodnou chybu. Směrování je možnosti, ve výchozím nastavení pouze zajišťuje adresu URL poskytnutou původní HttpRequestMessage. Minimální pokusy: 1
Maximální počet pokusů: 10
Zpoždění: 2s
3 Omezovač rychlosti (na koncový bod) Kanál omezovače rychlosti omezuje maximální počet souběžných požadavků odesílaných do závislosti. Fronta: 0
Dovolit: 1_000
4 Jistič (na koncový bod) Jistič blokuje provádění, pokud se zjistí příliš mnoho přímých selhání nebo časových limitů. Poměr selhání: 10 %
Minimální propustnost: 100
Doba trvání vzorkování: 30s
Doba trvání přestávky: 5s
5 Časový limit pokusu (na koncový bod) Kanál časového limitu pokusu omezuje každou dobu trvání pokusu o požadavek a vyvolá se, pokud je překročen. Časový limit: 10s

Přizpůsobení výběru trasy obslužné rutiny hedgingu

Při použití standardní obslužné rutiny hedgingu můžete přizpůsobit způsob, jakým jsou koncové body požadavku vybrány voláním různých rozšíření typu IRoutingStrategyBuilder . To může být užitečné pro scénáře, jako je testování A/B, kde chcete směrovat procento požadavků do jiného koncového bodu:

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

Předchozí kód:

  • Přidá obslužnou rutinu hedgingu IHttpClientBuilderdo .
  • Nakonfiguruje metodu IRoutingStrategyBuilder ConfigureOrderedGroups pro konfiguraci uspořádaných skupin.
  • EndpointGroup Přidá do orderedGroup koncového bodu 3 % požadavků https://example.net/api/experimental a 97 % požadavků do koncového https://example.net/api/stable bodu.
  • Nakonfiguruje IRoutingStrategyBuilder metodu ConfigureWeightedGroups tak, aby nakonfigurovali

Pokud chcete nakonfigurovat váženou skupinu, zavolejte ConfigureWeightedGroups metodu pro IRoutingStrategyBuilder typ. Následující příklad nakonfiguruje IRoutingStrategyBuilder použití ConfigureWeightedGroups metody ke konfiguraci vážených skupin.

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

Předchozí kód:

  • Přidá obslužnou rutinu hedgingu IHttpClientBuilderdo .
  • Nakonfiguruje IRoutingStrategyBuilder metodu ConfigureWeightedGroups tak, aby nakonfigurovala vážené skupiny.
  • SelectionMode Nastaví na WeightedGroupSelectionMode.EveryAttempt.
  • WeightedEndpointGroup Přidá do weightedGroup koncového bodu 33 % požadavkůhttps://example.net/api/a, 33 % požadavků na https://example.net/api/b koncový bod a 33 % požadavků na https://example.net/api/c koncový bod.

Tip

Maximální počet pokusů o hedgování přímo koreluje s počtem nakonfigurovaných skupin. Pokud máte například dvě skupiny, maximální počet pokusů je dva.

Další informace najdete v dokumentaci k Polly: Strategie zajištění odolnosti.

Často se konfiguruje buď uspořádaná skupina, nebo vážená skupina, ale je platná pro konfiguraci obojího. Použití seřazených a vážených skupin je užitečné ve scénářích, kdy chcete odeslat procento požadavků do jiného koncového bodu, například při testování A/B.

Přidání vlastních obslužných rutin odolnosti

Pokud chcete mít větší kontrolu, můžete přizpůsobit obslužné rutiny odolnosti pomocí AddResilienceHandler rozhraní API. Tato metoda přijímá delegáta, který konfiguruje ResiliencePipelineBuilder<HttpResponseMessage> instanci, která se používá k vytvoření strategií odolnosti.

Chcete-li nakonfigurovat pojmenovanou obslužnou rutinu odolnosti, zavolejte AddResilienceHandler metodu rozšíření s názvem obslužné rutiny. Následující příklad nakonfiguruje pojmenovanou obslužnou rutinu odolnosti s názvem "CustomPipeline".

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

Předchozí kód:

  • Přidá obslužnou rutinu odolnosti s názvem "CustomPipeline" jako pipelineName kontejneru služby.
  • Přidá strategii opakování s exponenciálním zpožováním, pěti opakováními a předvolbou jitter pro tvůrce odolnosti.
  • Přidá strategii jističe s dobou vzorkování 10 sekund, poměrem selhání 0,2 (20 %), minimální propustností tří a predikátem, který zpracovává stavové kódy RequestTimeout HTTP TooManyRequests pro tvůrce odolnosti.
  • Přidá strategii časového limitu s časovým limitem 5 sekund do tvůrce odolnosti.

Pro každou strategii odolnosti je k dispozici mnoho možností. Další informace najdete v dokumentaci k Polly: Strategie. Další informace o konfiguraci ShouldHandle delegátů najdete v dokumentaci Polly: Zpracování chyb v reaktivních strategiích.

Dynamické opětovné načítání

Polly podporuje dynamické opětovné načítání nakonfigurovaných strategií odolnosti. To znamená, že můžete změnit konfiguraci strategií odolnosti za běhu. Chcete-li povolit dynamické opětovné načítání, použijte příslušné AddResilienceHandler přetížení, které zveřejňuje ResilienceHandlerContext. Vzhledem k kontextu volejte EnableReloads odpovídající možnosti strategie odolnosti:

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

Předchozí kód:

  • Přidá obslužnou rutinu odolnosti s názvem "AdvancedPipeline" jako pipelineName kontejneru služby.
  • Povolí opětovné načtení "AdvancedPipeline" kanálu při každé změně pojmenovaných RetryStrategyOptions možností.
  • Načte pojmenované možnosti ze IOptionsMonitor<TOptions> služby.
  • Přidá strategii opakování s načtenými možnostmi do tvůrce odolnosti.

Další informace najdete v dokumentaci Polly: Rozšířená injektáž závislostí.

Tento příklad spoléhá na část možností, která je schopna změnit, například soubor appsettings.json . Zvažte následující appsettings.json soubor:

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

Teď si představte, že tyto možnosti byly svázané s konfigurací aplikace, svázat ho HttpRetryStrategyOptions s oddílem "RetryOptions" :

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

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

Další informace naleznete v tématu Možnosti vzor v .NET.

Příklad využití

Vaše aplikace spoléhá na injektáž závislostí k vyřešení ExampleClient a jeho odpovídajících HttpClient. Kód sestaví IServiceProvider a přeloží ho ExampleClient .

IHost host = builder.Build();

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

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

Předchozí kód:

Představte si situaci, kdy síť přestane fungovat nebo server přestane reagovat. Následující diagram ukazuje, jak by strategie odolnosti zvládly situaci vzhledem k ExampleClient tomu, že metoda a GetCommentsAsync metoda:

Příklad pracovního toku HTTP GET s kanálem odolnosti

Předchozí diagram znázorňuje:

  • Odešle ExampleClient požadavek HTTP GET do koncového /comments bodu.
  • Vyhodnocuje HttpResponseMessage se:
    • Pokud je odpověď úspěšná (HTTP 200), vrátí se odpověď.
    • Pokud je odpověď neúspěšná (HTTP ne-200), kanál odolnosti využívá nakonfigurované strategie odolnosti.

I když je to jednoduchý příklad, ukazuje, jak lze strategie odolnosti použít ke zpracování přechodných chyb. Další informace najdete v dokumentaci k Polly: Strategie.

Známé problémy

V následujících částech najdete podrobnosti o různých známých problémech.

Kompatibilita s balíčkem Grpc.Net.ClientFactory

Pokud používáte Grpc.Net.ClientFactory verzi nebo starší verzi 2.63.0 , může povolení standardní odolnosti nebo obslužných rutin pro klienta gRPC způsobit výjimku za běhu. Konkrétně zvažte následující ukázku kódu:

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

Předchozí kód má za následek následující výjimku:

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

Pokud chcete tento problém vyřešit, doporučujeme upgradovat na Grpc.Net.ClientFactory verzi 2.64.0 nebo novější.

K dispozici je kontrola času sestavení, která ověří, jestli používáte Grpc.Net.ClientFactory verzi nebo starší verzi 2.63.0 , a pokud kontrola vygeneruje upozornění kompilace. Upozornění můžete potlačit nastavením následující vlastnosti v souboru projektu:

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

Kompatibilita s .NET Application Insights

Pokud používáte .NET Application Insights, může povolení funkce odolnosti ve vaší aplikaci způsobit chybějící veškerou telemetrii Application Insights. K tomuto problému dochází, když je funkce odolnosti zaregistrovaná před službami Application Insights. Zvažte následující ukázku, která problém způsobuje:

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

Příčinou problému je následující chyba ve službě Application Insights, kterou je možné opravit registrací služeb Application Insights před funkčností odolnosti, jak je znázorněno níže:

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