Implementera kretsbrytarmönstret
Dricks
Det här innehållet är ett utdrag från eBook, .NET Microservices Architecture for Containerized .NET Applications, tillgängligt på .NET Docs eller som en kostnadsfri nedladdningsbar PDF som kan läsas offline.
Som tidigare nämnts bör du hantera fel som kan ta en varierande tid att återställa från, vilket kan inträffa när du försöker ansluta till en fjärrtjänst eller resurs. Att hantera den här typen av fel kan förbättra stabiliteten och motståndskraften hos ett program.
I en distribuerad miljö kan anrop till fjärrresurser och tjänster misslyckas på grund av tillfälliga fel, till exempel långsamma nätverksanslutningar och tidsgränser, eller om resurserna svarar långsamt eller är tillfälligt otillgängliga. Dessa fel korrigerar sig vanligtvis efter en kort tid, och ett robust molnprogram bör vara redo att hantera dem med hjälp av en strategi som "Försök igen".
Det kan dock också finnas situationer där fel beror på oväntade händelser som kan ta mycket längre tid att åtgärda. Felen kan variera i allvarlighetsgrad, från en partiell förlust av anslutning till fullständigt fel i en tjänst. I dessa situationer kan det vara meningslöst för ett program att kontinuerligt försöka utföra en åtgärd som sannolikt inte lyckas.
I stället bör programmet kodas för att acceptera att åtgärden har misslyckats och hantera felet i enlighet med detta.
Om du använder Http-återförsök oförsiktigt kan det leda till att du skapar en DoS-attack (Denial of Service) i din egen programvara. Eftersom en mikrotjänst misslyckas eller går långsamt kan flera klienter upprepade gånger försöka utföra misslyckade begäranden igen. Det skapar en farlig risk för exponentiellt ökande trafik som riktas mot den misslyckade tjänsten.
Därför behöver du någon form av försvarsbarriär så att överdrivna begäranden stoppas när det inte är värt att fortsätta försöka. Försvarsbarriären är just kretsbrytaren.
Kretsbrytarmönstret har ett annat syfte än "Återförsöksmönstret". Med "Återförsöksmönstret" kan ett program försöka utföra en åtgärd igen i väntan på att åtgärden ska lyckas. Kretsbrytarmönstret förhindrar att ett program utför en åtgärd som sannolikt kommer att misslyckas. Ett program kan kombinera dessa två mönster. Logiken för återförsök bör dock vara känslig för alla undantag som returneras av kretsbrytaren och bör avbryta återförsök om kretsbrytaren anger att ett fel inte är tillfälligt.
Implementera kretsbrytarmönster med IHttpClientFactory
och Polly
Precis som när du implementerar återförsök är den rekommenderade metoden för kretsbrytare att dra nytta av beprövade .NET-bibliotek som Polly och dess interna integrering med IHttpClientFactory
.
Att lägga till en kretsbrytarprincip i din IHttpClientFactory
utgående mellanprogramspipeline är lika enkelt som att lägga till en enda inkrementell kod i det du redan har när du använder IHttpClientFactory
.
Det enda tillägget här till koden som används för HTTP-anropsförsök är koden där du lägger till kretsbrytarprincipen i listan över principer som ska användas, enligt följande inkrementella kod.
// Program.cs
var retryPolicy = GetRetryPolicy();
var circuitBreakerPolicy = GetCircuitBreakerPolicy();
builder.Services.AddHttpClient<IBasketService, BasketService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) // Sample: default lifetime is 2 minutes
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy);
Metoden AddPolicyHandler()
är det som lägger till principer för de objekt som HttpClient
du ska använda. I det här fallet lägger den till en Polly-princip för en kretsbrytare.
Om du vill ha en mer modulär metod definieras kretsbrytarprincipen i en separat metod med namnet GetCircuitBreakerPolicy()
, enligt följande kod:
// also in Program.cs
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}
I kodexemplet ovan konfigureras kretsbrytarprincipen så att den bryter eller öppnar kretsen när det har uppstått fem på varandra följande fel vid återförsök av Http-begäranden. När det händer bryts kretsen i 30 sekunder: under den perioden misslyckas anropen omedelbart av kretsbrytaren i stället för att faktiskt placeras. Principen tolkar automatiskt relevanta undantag och HTTP-statuskoder som fel.
Kretsbrytare bör också användas för att omdirigera begäranden till en återställningsinfrastruktur om du har problem med en viss resurs som distribueras i en annan miljö än klientprogrammet eller tjänsten som utför HTTP-anropet. På så sätt kan klientprogrammen omdirigeras till reservtjänsterna om det uppstår ett avbrott i datacentret som bara påverkar dina serverdelsmikrotjänster men inte dina klientprogram. Polly planerar en ny princip för att automatisera det här redundansprincipscenariot .
Alla dessa funktioner gäller för fall där du hanterar redundansväxlingen inifrån .NET-koden, i stället för att låta den hanteras automatiskt av Azure, med platstransparens.
När du använder HttpClient från användningssynpunkt behöver du inte lägga till något nytt här eftersom koden är densamma som när du använder HttpClient
med IHttpClientFactory
, som du ser i föregående avsnitt.
Testa Http-återförsök och kretsbrytare i eShopOnContainers
När du startar lösningen eShopOnContainers i en Docker-värd måste den starta flera containrar. Vissa av containrarna är långsammare att starta och initiera, till exempel SQL Server-containern. Detta gäller särskilt första gången du distribuerar eShopOnContainers-programmet till Docker eftersom det måste konfigurera avbildningarna och databasen. Det faktum att vissa containrar startar långsammare än andra kan leda till att resten av tjänsterna ursprungligen genererar HTTP-undantag, även om du anger beroenden mellan containrar på docker-compose-nivån, enligt beskrivningen i föregående avsnitt. Dessa docker-compose-beroenden mellan containrar är bara på processnivå. Containerns startpunktsprocess kan startas, men SQL Server kanske inte är redo för frågor. Resultatet kan vara en kaskad av fel och programmet kan få ett undantag när det försöker använda just den containern.
Du kan också se den här typen av fel vid start när programmet distribueras till molnet. I så fall kan orkestrerare flytta containrar från en nod eller virtuell dator till en annan (dvs. starta nya instanser) när de balanserar antalet containrar mellan klustrets noder.
Det sätt som "eShopOnContainers" löser dessa problem när du startar alla containrar är genom att använda återförsöksmönstret som illustrerades tidigare.
Testa kretsbrytaren i eShopOnContainers
Det finns några sätt att bryta/öppna kretsen och testa den med eShopOnContainers.
Ett alternativ är att sänka det tillåtna antalet återförsök till 1 i kretsbrytarprincipen och distribuera om hela lösningen till Docker. Med ett enda nytt försök finns det en god chans att en HTTP-begäran misslyckas under distributionen, kretsbrytaren öppnas och du får ett fel.
Ett annat alternativ är att använda anpassade mellanprogram som implementeras i basket-mikrotjänsten. När det här mellanprogrammet är aktiverat fångar det upp alla HTTP-begäranden och returnerar statuskod 500. Du kan aktivera mellanprogrammet genom att göra en GET-begäran till den misslyckade URI:n, som följande:
GET http://localhost:5103/failing
Den här begäran returnerar det aktuella tillståndet för mellanprogrammet. Om mellanprogrammet är aktiverat returnerar begäran statuskod 500. Om mellanprogrammet är inaktiverat finns det inget svar.GET http://localhost:5103/failing?enable
Den här begäran aktiverar mellanprogrammet.GET http://localhost:5103/failing?disable
Den här begäran inaktiverar mellanprogrammet.
När programmet till exempel körs kan du aktivera mellanprogrammet genom att göra en begäran med hjälp av följande URI i valfri webbläsare. Observera att beställningsmikrotjänsten använder port 5103.
http://localhost:5103/failing?enable
Du kan sedan kontrollera statusen med hjälp av URI http://localhost:5103/failing
: n , enligt bild 8–5.
Bild 8-5. Kontrollerar tillståndet för ASP.NET mellanprogram – i det här fallet inaktiverat.
Nu svarar basket-mikrotjänsten med statuskod 500 när du anropar den.
När mellanprogrammet körs kan du prova att göra en beställning från MVC-webbappen. Eftersom begäranden misslyckas öppnas kretsen.
I följande exempel kan du se att MVC-webbprogrammet har ett catch-block i logiken för att göra en beställning. Om koden fångar upp ett undantag med öppen krets visas ett användarvänligt meddelande som uppmanar användaren att vänta.
public class CartController : Controller
{
//…
public async Task<IActionResult> Index()
{
try
{
var user = _appUserParser.Parse(HttpContext.User);
//Http requests using the Typed Client (Service Agent)
var vm = await _basketSvc.GetBasket(user);
return View(vm);
}
catch (BrokenCircuitException)
{
// Catches error when Basket.api is in circuit-opened mode
HandleBrokenCircuitException();
}
return View();
}
private void HandleBrokenCircuitException()
{
TempData["BasketInoperativeMsg"] = "Basket Service is inoperative, please try later on. (Business message due to Circuit-Breaker)";
}
}
Här är en sammanfattning. Återförsöksprincipen försöker flera gånger att göra HTTP-begäran och hämtar HTTP-fel. När antalet återförsök når det högsta antal som angetts för circuit breaker-principen (i det här fallet 5) genererar programmet en BrokenCircuitException. Resultatet är ett vänligt meddelande, som visas i bild 8-6.
Bild 8-6. Kretsbrytare som returnerar ett fel till användargränssnittet
Du kan implementera olika logik för när kretsen ska öppnas/brytas. Eller så kan du prova en HTTP-begäran mot en annan serverdelsmikrotjänst om det finns ett reservdatacenter eller ett redundant serverdelssystem.
Slutligen är en annan möjlighet för CircuitBreakerPolicy
att använda Isolate
(som tvingar öppna och håller öppna kretsen) och Reset
(som stänger den igen). Dessa kan användas för att skapa en HTTP-slutpunkt för verktyget som anropar Isolera och återställ direkt på principen. En sådan HTTP-slutpunkt kan också användas, lämpligt skyddad, i produktion för att tillfälligt isolera ett nedströmssystem, till exempel när du vill uppgradera det. Eller så kan den köra kretsen manuellt för att skydda ett nedströmssystem som du misstänker är fel.
Ytterligare resurser
- Kretsbrytarmönster
https://learn.microsoft.com/azure/architecture/patterns/circuit-breaker