Il pattern Circuit Breaker è di fondamentale importanza in un'architettura service-oriented, perchè impedisce che il malfunzionamento di un servizio non si ripercuota a catena su tutti gli altri componenti che lo utilizzano.
Cerchiamo di comprenderne la logica guardando l'immagine in basso, e immaginiamo di avere un servizio che sia momentaneamente non disponibile.
Quando effettuiamo una chiamata tramite il circuit breaker, inizialmente in stato Closed, il servizio viene effettivamente invocato e, pertanto, solleverà un'eccezione - per esempio un errore HTTP 500.
Dopo un certo numero di errori consecutivi, il circuito passerà in stato Open. In questa condizione, la chiamata viene immediatamente bypassata, e ci verrà immediatamente sollevata un'eccezione di tipo CircuitBreakerException.
Il fatto che il circuito fallisca subito è fondamentale. Pensiamo infatti a cosa potrebbe accadere con una risorsa che vada in timeout: ogni utente dovrebbe attendere diversi secondi prima di vedersi restituire un messaggio di errore. In stato Open, invece, possiamo evitare completamente di chiamare il servizio in errore, magari indicando che solo una specifica parte della pagina non può essere popolata correttamente a causa di questo problema, ma comunque salvaguardando la user experience.
Una volta trascorso un certo lasso di tempo, il circuito si sposterà in stato Half-Closed: in questa modalità, proverà di nuovo a chiamare il servizio, ma se dovesse verificarsi ancora un errore, tornerà immediatamente in stato Open. Diversamente si riporterà in stato Closed.
La libreria Polly (https://github.com/App-vNext/Polly) che abbiamo iniziato a utilizzare nello script precedente, possiede già out-of-the-box un'implementazione di circuit breaker, che ci permette molto facilmente di integrare la logica che abbiamo visto in precedenza nella nostra applicazione.
Dopo aver installato il package NuGet, il primo passo è quello di registrare e configurare il Circuit Breaker nel metodo ConfigureServices della classe Startup.
public void ConfigureServices(IServiceCollection services) { // .. altri servizi qui .. var breaker = Policy .Handle<Exception>() .CircuitBreakerAsync(2, TimeSpan.FromSeconds(10)); services.AddSingleton<CircuitBreakerPolicy>(breaker); }
Nel nostro caso, il circuito si aprirà automaticamente dopo 2 exception di qualsiasi tipo, e rimarrà in questo stato per un TimeSpan di 10 secondi. E' assolutamente importante registrare il servizio come Singleton, perché l'istanza deve essere condivisa tra tutti i chiamanti e lo stato deve essere persistito tra le varie request.
A questo punto, nel controller, possiamo recuperare l'istanza di CircuitBreakerPolicy e utilizzarla per chiamare il servizio esterno.
public HomeController(IContentService content, CircuitBreakerPolicy breaker) { _content = content; _breaker = breaker; } public async Task<IActionResult> Index() { string[] articles; try { articles = await _breaker.ExecuteAsync( () => _content.LoadArticlesAsync()); } catch (Exception) { articles = new[] { "Articoli non disponibili al momento" }; } ViewBag.State = _breaker.CircuitState; return View(articles); }
La action Index, tramite il metodo ExecuteAsync del circuit breaker, può effettuare una chiamata protetta al metodo LoadArticlesAsync: quando questa fallisce, l'esito sarà memorizzato nella macchina a stati del circuito, che si comporterà secondo la logica che abbiamo descritto in precedenza.
Per capire il beneficio di questo pattern, possiamo provare a eseguire il codice allegato allo script, in cui LoadArticlesAsync fallisce dopo un timeout di 10 secondi. Le prime due esecuzioni della pagina saranno estremamente lente, e visualizzeranno il messaggio "Articoli non disponibili".
Successivamente, il messaggio non cambierà - abbiamo creato il servizio in modo che fallisca sempre! - ma il caricamento della pagina sarà infinitamente più veloce. Se la pagina in questione fosse una dashboard, che invoca diversi servizi prima di mostrare un report all'utente, possiamo perfettamente intuire quanto questo possa rappresentare un beneficio per la user experience.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Eseguire un metodo asincrono dopo il set di una proprietà in Blazor 8
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Come EF 8 ha ottimizzato le query che usano il metodo Contains
Eseguire una GroupBy per entity in Entity Framework
Modificare i metadati nell'head dell'HTML di una Blazor Web App
Usare un KeyedService di default in ASP.NET Core 8
Accesso sicuro ai secrets attraverso i file in Azure Container Apps
Specificare il versioning nel path degli URL in ASP.NET Web API
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Ottenere il contenuto di una cartella FTP con la libreria FluentFTP
Implementare il throttling in ASP.NET Core
Gestire undefined e partial nelle reactive forms di Angular
I più letti di oggi
- Utilizzare Docker Compose con Azure App Service
- Utilizzare QuickGrid di Blazor con Entity Framework
- Modernizzare le applicazioni WPF e Windows Forms con Blazor
- ASP 3 per esempi
- annunciato #netstandard 2.1. .NET Core lo supporterà a partire da #netcore3, così come le prossime versione di #xamarin, #mono e #unity.il supporto per #netfx 4.8, invece, non ci sarà. https://aspit.co/bq2
- Steel Style CheckBox per Silverlight 4.0