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á doorderedGroup
koncového bodu 3 % požadavkůhttps://example.net/api/experimental
a 97 % požadavků do koncovéhohttps://example.net/api/stable
bodu.- Nakonfiguruje
IRoutingStrategyBuilder
metoduConfigureWeightedGroups
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
metoduConfigureWeightedGroups
tak, aby nakonfigurovala vážené skupiny. SelectionMode
Nastaví naWeightedGroupSelectionMode.EveryAttempt
.WeightedEndpointGroup
Přidá doweightedGroup
koncového bodu 33 % požadavkůhttps://example.net/api/a
, 33 % požadavků nahttps://example.net/api/b
koncový bod a 33 % požadavků nahttps://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"
jakopipelineName
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
HTTPTooManyRequests
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"
jakopipelineName
kontejneru služby. - Povolí opětovné načtení
"AdvancedPipeline"
kanálu při každé změně pojmenovanýchRetryStrategyOptions
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:
- Vytvoří z objektu IServiceProvider ServiceCollection.
- Vyřeší chybu
ExampleClient
z IServiceProvider - Zavolá metodu
GetCommentsAsync
ExampleClient
pro získání komentářů. - Zapíše každý komentář do konzoly.
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ř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();