Implementação de Políticas de Resiliência com Polly no .NET

Introdução ao Polly

Polly é uma biblioteca para aplicações .NET que fornece um conjunto de políticas de tratamento de falhas, permitindo a implementação de mecanismos como repetição, timeout e disjuntores (circuit breakers). Essa biblioteca ajuda os desenvolvedores a criar aplicações mais robustas e tolerantes a falhas.

Estratégia de Repetição (Retry)

Essa política permite a repetição automática de operações quando ocorrem exceções específicass.


using System;
using Polly;

class Program
{
    static void Executar()
    {
        var politicaDeRepeticao = Policy
            .Handle<InvalidOperationException>()
            .Or<TimeoutException>()
            .Retry(2, (excecao, tentativa) =>
            {
                Console.WriteLine($"Falha detectada. Tentativa {tentativa}");
                Console.WriteLine($"Tipo de exceção: {excecao.GetType().Name}");
            });

        try
        {
            politicaDeRepeticao.Execute(() =>
            {
                var valor = 1;
                var divisor = 0;
                var resultado = valor / divisor; // Isso lançará DivideByZeroException, não tratada acima.
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exceção não tratada: {ex.GetType().Name}");
        }
    }
}

Para adicionar intervalos entre as repetições:


using System;
using System.Threading.Tasks;
using Polly;

class Program
{
    static async Task ExecutarComIntervalo()
    {
        var politicaComEspera = Policy
            .Handle<DivideByZeroException>()
            .WaitAndRetryAsync(new[]
            {
                TimeSpan.FromMilliseconds(500),
                TimeSpan.FromSeconds(2),
                TimeSpan.FromSeconds(4)
            }, (excecao, tempo, contexto) =>
            {
                Console.WriteLine($"Erro ocorrido. Aguardando {tempo.TotalSeconds} segundos.");
            });

        try
        {
            await politicaComEspera.ExecuteAsync(async () =>
            {
                await Task.Delay(100);
                var numerador = 10;
                var denominador = 0;
                var quociente = numerador / denominador;
            });
        }
        catch (Exception excecao)
        {
            Console.WriteLine($"Operação falhou: {excecao.Message}");
        }
    }
}

Estratégia de Timeout

Define um tempo limite para operações, cancelando automaticamente se não forem concluídas a tempo.


using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Polly;
using Polly.Timeout;

class Program
{
    static async Task TestarTimeout()
    {
        var politicaDeTimeout = Policy
            .TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(2), TimeoutStrategy.Optimistic);

        try
        {
            var clienteHttp = new HttpClient();
            var resposta = await politicaDeTimeout.ExecuteAsync(async (cancelToken) =>
            {
                return await clienteHttp.GetAsync("http://api.exemplo.com", cancelToken);
            }, CancellationToken.None);

            if (resposta.IsSuccessStatusCode)
            {
                var conteudo = await resposta.Content.ReadAsStringAsync();
                Console.WriteLine(conteudo);
            }
        }
        catch (TimeoutRejectedException)
        {
            Console.WriteLine("A operação excedeu o tempo limite.");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Erro de rede: {ex.Message}");
        }
    }
}

Estratégia de Disjuntor (Circuit Breaker)

Evita sobrecarregar serviços com falhas repetidas, abrindo o circuito após um número de erros consecutivos.


using System;
using System.Threading.Tasks;
using Polly;
using Polly.CircuitBreaker;

class Program
{
    static async Task DemonstrarCircuitBreaker()
    {
        var politicaDisjuntor = Policy
            .Handle<Exception>()
            .CircuitBreakerAsync(3, TimeSpan.FromSeconds(10));

        for (int tentativa = 0; tentativa < 15; tentativa++)
        {
            try
            {
                await Task.Delay(TimeSpan.FromSeconds(0.5));
                await politicaDisjuntor.ExecuteAsync(async () =>
                {
                    // Simular uma operação que falha
                    throw new InvalidOperationException("Falha simulada");
                });
            }
            catch (BrokenCircuitException)
            {
                Console.WriteLine($"Circuito aberto na tentativa {tentativa}. Operação bloqueada.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Tentativa {tentativa} falhou: {ex.Message}");
            }
        }
    }
}

Integração com HttpClient no ASP.NET Core

A biblioteca Microsoft.Extensions.Http.Polly simplifica a aplicação de políticas do Polly em HttpClientFactory.


using Microsoft.Extensions.DependencyInjection;
using Polly;
using System;
using System.Net.Http;

// Configuração no Startup.cs ou similar
public void ConfigureServices(IServiceCollection servicos)
{
    var politicaRepeticao = Policy
        .Handle<HttpRequestException>()
        .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
        .RetryAsync(2);

    var politicaTimeout = Policy.TimeoutAsync<HttpResponseMessage>(5);

    servicos.AddHttpClient("ServicoExterno", cliente =>
    {
        cliente.BaseAddress = new Uri("http://servico-externo.com");
    })
    .AddPolicyHandler(politicaRepeticao)
    .AddPolicyHandler(politicaTimeout);
}

Referências

Tags: Polly .NET C# httpclient ASP.NET Core

Publicado em 6-24 03:28