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 aConfigureOrderedGroups
rendezett csoportok konfigurálásához. - Hozzáad egy olyan
EndpointGroup
fájltorderedGroup
, amely a kérések 3%-át ahttps://example.net/api/experimental
végpontra irányítja, a kérések 97%-át pedig ahttps://example.net/api/stable
végpontra. -
IRoutingStrategyBuilder
A metódus használatával konfigurálja aConfigureWeightedGroups
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 aConfigureWeightedGroups
súlyozott csoportok konfigurálásához. - A következőre állítja a
SelectionMode
következőtWeightedGroupSelectionMode.EveryAttempt
: . - Hozzáadja
WeightedEndpointGroup
aweightedGroup
végponthoz a kérések 33%-át, ahttps://example.net/api/a
kérések 33%-át ahttps://example.net/api/b
végponthoz, a kérések 33%-át pedig ahttps://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ódokatTooManyRequests
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átottRetryStrategyOptions
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 aExampleClient
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:
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.ClientFactory
2.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();