Polly : fiabilisez vos développements grâce au pattern circuit breaker

Polly est une bibliothèque de résilience et de gestion des défaillances réseau en .NET.

Elle permet aux développeurs d'exprimer des politiques telles que « Retry », « Circuit Breaker », « Timeout », « Bulkhead Isolation » et « Fallback » d'une manière fluide et sûre. Concentrons-nous dans un premier temps sur la notion de « Circuit Breaker ». A la différence d’une politique telle que « Retry » qui réessayera une opération un nombre de fois défini, le pattern « Circuit Breaker » est là pour optimiser la réponse à un défaut persistant du réseau ou du système.  

Le pattern circuit breaker

Son objectif est de prévenir une opération que l’application sait être un échec au moment de l’appel. Nous parlons de pattern « Circuit Breaker » car il englobe un ensemble de mécanismes permettant de définir à quel moment le système doit se mettre en action, pendant combien de temps, à quel moment il redeviendra stable ou encore quelle réponse effectuer en cas d’échec. Pour grossir le trait, nous pouvons considérer que l’objectif du circuit breaker est de réaliser un proxy surveillant les défaillances afin d’appliquer la bonne stratégie, au bon moment. Inutile de perdre du temps processeur pour une action vouée à lever une exception ! C’est un mécanisme puissant permettant à tout développeur d’obtenir une réponse élégante à une erreur inattendue, et c’est bien cette notion qui nous intéresse dans cet article.  

Une state machine à lui tout seul 

Le pattern « Circuit Breaker » et son implémentation dans Polly est avant tout une machine à état, trois pour être exact :

  • Closed : le circuit est fermé et fonctionne normalement. Il gère les compteurs de succès et d’erreurs.
  • Open : Le circuit est ouvert, il applique donc la stratégie demandée en attendant la fin d’un timeout, par exemple.
  • Half-Open : le circuit vérifie si un retour à la normale est possible. En cas de succès, il passe en état « closed ». Dans le cas contraire, le circuit retourne en état « open »

 

Et dans la pratique ? Implémentation du Pattern Circuit breaker 

Le cas d’usage est simple : nous devons récupérer des informations provenant d’une API qui lorsqu’elle n’est plus joignable, renvoie des données de mock. Avant toute chose, il est nécessaire d’installer le package : dotnet add package Polly Vous devez configurer Polly pour qu’il prenne en compte votre stratégie « Circuit Breaker » :

1. private static readonly AsyncCircuitBreakerPolicy CircuitBreakerPolicy = Policy 2. .Handle<Exception>() 3. .CircuitBreakerAsync( 4. 2, //Nombre d'exceptions avant le passage en mode open 5. TimeSpan.FromSeconds(30), //Temps avant d'essayer de rétablir la connexion 6. OnBreak, 7. OnReset); 8. 9. private static void OnReset() 10. { 11. Console.WriteLine("Circuit is on Reset"); 12. } 13. 14. private static void OnBreak(Exception arg1, TimeSpan arg2) 15. { 16. 17. Console.WriteLine("Circuit is on Break"); 18. }

Tout d’abord, paramétrez le nombre critique d’exceptions et le temps du timeout. Vous pouvez également configurer une action sur les events au moment du break et du reset. Une fois la stratégie définie, il vous faut l’insérer au moment de l’appel à votre API :

1. public async Task<List<WeatherForecast>> GetWeatherForecast() 2. { 3. 4. try 5. { 6. Console.WriteLine($"Circuit Breaker status {CircuitBreakerPolicy.CircuitState}"); 7. var response = await CircuitBreakerPolicy.ExecuteAsync(() => ExecuteGetRequest()); 8. 9. return response; 10. 11. } 12. catch (Exception ex) 13. { 14. return new List<WeatherForecast>(); 15. } 16. 17. 18. } 19. 20. private async Task<List<WeatherForecast>> ExecuteGetRequest() 21. { 22. Console.WriteLine("Call Api"); 23. return await _client.GetFromJsonAsync<List<WeatherForecast>>("/WeatherForecast"); 24. }

Si nous testons le pattern maintenant, nous obtiendrons ce résultat :

 

Puis, nous coupons l’API :

 

Voici ce qu’il se produit dans la console de debug :

 

C’est un bon début, mais j’aimerais désormais gérer une réponse plus adéquate qu’une liste vide.  

La notion de Fallback

Dans Polly, il est donc possible de gérer une réponse type au travers d’une autre Policy nommée « Fallback ». Cela se traduit dans le code par la création d’une deuxième Policy que l’on va coupler à l’aide de l’option « WrapAsync » avec la Policy de « Circuit Breaker » :

private static List<WeatherForecast> _lastWeatherForecasts = new List<WeatherForecast>(); private static readonly AsyncCircuitBreakerPolicy CircuitBreakerPolicy = Policy .Handle<Exception>() .CircuitBreakerAsync( 2, //Nombre d'exceptions avant le passage en mode open TimeSpan.FromSeconds(30), //Temps avant d'essayer de rét OnBreak, OnReset); private readonly AsyncPolicyWrap<List<WeatherForecast>> _processHttpMessagePolicy; private readonly ILogger<WeatherForecastHttpRepository> _logger; public WeatherForecastHttpRepository(HttpClient client, ILogger<WeatherForecastHttpRepository> logger) { _client = client; _logger = logger; _processHttpMessagePolicy = Policy<List<WeatherForecast>> .Handle<Exception>() .FallbackAsync<List<WeatherForecast>>(fallbackAction: async ct => _lastWeatherForecasts, onFallbackAsync: async e => { Console.WriteLine("Return Fallback"); }).WrapAsync(CircuitBreakerPolicy); }

La policy Fallback prend ainsi le relai de la réponse en cas de défaillance réseau et cela à partir du premier échec :

 

Dans mon exemple, j’ai renvoyé la dernière réponse connue de l’API :

 

Pour conclure, Polly et le pattern « Circuit Breaker » sont de formidables outils pour gérer de manière élégante les défaillances de réseau, mais aussi de repository en tant que tel. Au-delà de gérer efficacement les réponses, ils apportent un gain indéniable de performance avec le renvoi immédiat d’une réponse en mode dégradé, lorsque survient, et aussi longtemps que survient un incident. Sur des plateformes temps réel, cela peut faire toute la différence. Même sur des applications traditionnelles, Polly peut aussi prouver son utilité, afin d’éviter l’attente utilisateur sur une service que l’on connait défaillant. Pour aller plus loin, Polly a une notion avancée de « Circuit Breaker » permettant une gestion plus fine des passages entre états. Il existe également une extension du package HttpClient : Microsoft.Extensions.Http.Polly.  

Sources : csannierfr/UsePolly (github.com) GitHub - App-vNext/Polly. Circuit Breaker pattern - Cloud Design Patterns | Microsoft Docs CircuitBreaker (martinfowler.com)