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);
}