Polly : la stratégie de résilience pour vos appels externes

Polly est une petite bibliothèque open‑source (NuGet) qui vous permet d'ajouter des stratégies de résilience à votre code, sans y passer tout votre temps.

On l'utilise surtout pour :

  • les requêtes HTTP qui échouent parfois,
  • les appels à des bases de données ou services externes,
  • n'importe quelle opération qui peut lancer une exception et que vous ne voulez pas laisser tomber sur le premier échec.

En gros, Polly vous donne un gabarit (retry, circuit breaker, timeout, fallback…) que vous appliquez à votre logique métier. Pas de code répétitif, juste un objet Policy que vous composez.

Installation

NuGet
# Depuis la console du gestionnaire de packages
Install-Package Polly

# ou avec .NET CLI
dotnet add package Polly

Polly ne dépend d'aucun framework particulier, donc vous pouvez l'utiliser dans un projet .NET Core, .NET 5/6/7 ou même Mono/Xamarin. Si vous utilisez ASP.NET Core, il y a aussi Microsoft.Extensions.Http.Polly qui intègre Polly directement aux clients HttpClientFactory.

Principe de fonctionnement

Définir une politique → Envelopper votre code → La politique gère les échecs
var retryPolicy = Policy
  // Target IOException
  .Handle<IOException>()
  .WaitAndRetry(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
  • Handle<TException>() : spécifie le type d'exception à intercepter
  • WaitAndRetry(...) : définit le nombre de tentatives et le délai entre chaque essai

Pour appliquer cette politique :

retryPolicy.Execute(() => File.ReadAllText("data.txt"));

Si File.ReadAllText lève une IOException, Polly va automatiquement la retry 3 fois avec un délai croissant (2s, 4s, 8s).

Les types de politiques les plus courants
TypeQuand l'utiliser ?Exemple rapide
RetryÉchecs temporaires (timeout, 5xx)Policy.Handle<Exception>().Retry(3)
Circuit BreakerBesoin d'éviter de surcharger un service qui est déjà en pannePolicy.Handle<Exception>().CircuitBreaker(2, TimeSpan.FromSeconds(30))
TimeoutLimiter la durée d'une opérationPolicy.Timeout(TimeSpan.FromSeconds(5))
FallbackRetourner une valeur par défaut ou lancer une autre action quand tout échouePolicy.Handle<Exception>().Fallback(() => "fallback")

Exemples concrets

Retry + Timeout sur un appel HTTP
using Polly;
using System.Net.Http;
 
// 1) Create an HttpClient (or use IHttpClientFactory)
var client = new HttpClient();

// 2) Define the policies
var timeoutPolicy = Policy.Timeout<HttpResponseMessage>(TimeSpan.FromSeconds(3));
var retryPolicy = Policy.Handle<Exception>()
             .WaitAndRetryAsync(
               retryCount: 3,
               sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(200 * attempt)
             );

// 3) Compose the two policies
var policyWrap = Policy.WrapAsync(retryPolicy, timeoutPolicy);

// 4) Execute the HTTP call with the policy
var response = await policyWrap.ExecuteAsync(async () =>
{
  return await client.GetAsync("https://example.com/api/data");
});

Console.WriteLine($"Status : {response.StatusCode}");

si vous utilisez ASP.NET Core, créez un HttpClient via IHttpClientFactory, puis enregistrez la politique dans le conteneur. Polly s'intègre automatiquement.

Circuit Breaker + Fallback
var circuitBreaker = Policy.Handle<Exception>()
                           .CircuitBreakerAsync(
                               exceptionsAllowedBeforeBreaking: 2,
                               durationOfBreak: TimeSpan.FromSeconds(10)
                           );

var fallback = Policy<string>
               .Handle<Exception>()
               .FallbackAsync("service unavailable");

var policyWrap = Policy.WrapAsync(fallback, circuitBreaker);

string result = await policyWrap.ExecuteAsync(async () =>
{
  // Simulate a call to an external service that may fail
  return await SomeExternalService.GetDataAsync();
});

Console.WriteLine(result);

Dans cet exemple :

  • Si le service lève une exception deux fois de suite, Polly ouvre le circuit et bloque les appels pendant 10 s.
  • Pendant que le circuit est ouvert, la fallback renvoie immédiatement "service unavailable" sans appeler le service.
Retry avec back‑off exponentiel
var retryPolicy = Policy.Handle<Exception>()
    .WaitAndRetryAsync(
        retryCount: 5,
        sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))
    );

await retryPolicy.ExecuteAsync(async () =>
{
    await SomeOperationThatMayFail();
});
Intégration avec HttpClientFactory (ASP.NET Core)
services.AddHttpClient("myclient")
        .AddPolicyHandler(HttpPolicyExtensions
            .HandleTransientHttpError()
            .RetryAsync(3))
        .AddPolicyHandler(HttpPolicyExtensions
            .HandleTransientHttpError()
            .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

HandleTransientHttpError() HandleTransientHttpError() est un raccourci qui cible les 408/500/502/503/504 et TimeoutException.

Bonnes pratiques

RèglePourquoi
Ne retry que sur des erreurs transitoiresUn retry sur une erreur logique (404) n'a aucun sens.
Limitez le nombre d'essaisTrop de tentatives peuvent retarder votre application inutilement.
Combinez avec un circuit breakerSi la cible est réellement hors service, éviter de saturer votre réseau.
Ajoutez un fallbackOffrez une expérience utilisateur plus fluide plutôt que de tout planter.
Testez vos politiquesUtilisez des tests unitaires (xUnit + Polly.Testing) pour valider le comportement en cas d'échec.

En résumé

Polly vous donne un framework léger mais puissant pour rendre vos appels externes plus robustes : retry, timeout, circuit breaker, fallback… Vous écrivez votre logique métier une seule fois et vous appliquez la politique en un appel Execute. C'est simple à mettre en place, très flexible, et ça s'intègre parfaitement dans l'écosystème .NET.

Prêt à rendre vos services plus résilients ? Lancez les commandes NuGet, créez votre première Policy et voyez la différence !

Écrit le 2025-08-20