Hoje vamos falar sobre resiliência em aplicações e como implementar políticas de retry, circuit breaker e outras técnicas usando a biblioteca Polly no .NET 8.
O que é Resiliência em Software?
Resiliência em software é a capacidade de uma aplicação continuar funcionando adequadamente mesmo quando enfrenta falhas temporárias ou problemas de infraestrutura. Em um mundo de microserviços e APIs distribuídas, falhas transitórias são comuns - timeouts de rede, serviços temporariamente indisponíveis, limitações de taxa (rate limiting), entre outros.
O que é o Polly?
Polly é uma biblioteca .NET que permite implementar políticas de resiliência e tratamento de falhas transitórias de forma elegante e configurável. Com Polly, você pode adicionar comportamentos como:
- Retry: Tentar novamente após uma falha
- Circuit Breaker: Evitar chamadas quando um serviço está falhando consistentemente
- Timeout: Definir tempo limite para operações
- Fallback: Comportamento alternativo em caso de falha
- Bulkhead Isolation: Isolar recursos para evitar propagação de falhas
- Cache: Armazenar resultados para evitar chamadas desnecessárias
Por que adotar o Polly?
Antes de adotarmos o Polly, lidávamos com falhas transitórias em nosso código de forma manual e pouco estruturada.
Um exemplo era o uso de goto para implementar tentativas de repetição (retry) em chamadas onde um token vencia e precisva pegar um novo. Apesar de funcionar em alguns cenários, essa abordagem tornava o código difícil de ler, de manter e de evoluir.
O Polly surgiu como uma alternativa muito mais limpa, flexível e padronizada. Com ele conseguimos substituir blocos de código complexos (e até "gambiarras") por políticas declarativas, configuráveis e fáceis de entender.
Isso trouxe vários benefícios:
- Legibilidade: O código ficou mais claro e expressivo.
- Padronização: Passamos a tratar falhas de forma consistente em diferentes partes da aplicação.
- Evolução: É simples trocar ou combinar políticas (ex.: Retry + Circuit Breaker) sem precisar reescrever a lógica do zero.
- Manutenção: Reduzimos a chance de erros escondidos em tratamentos manuais.
Instalação
Para começar a usar o Polly em seu projeto .NET 8, instale o pacote NuGet:
dotnet add package Polly
dotnet add package Microsoft.Extensions.Http.Polly
Implementação Prática
Vamos criar exemplos práticos de como usar o Polly em diferentes cenários.
1. Retry Simples
Começando com um exemplo básico de retry:
using Polly;
public class ProdutoService
{
private readonly HttpClient _httpClient;
public async Task<Produto> ObterProdutoAsync(int id)
{
// Define política de retry: 3 tentativas
var retryPolicy = Policy
.Handle<HttpRequestException>()
.RetryAsync(3, onRetry: (exception, retryCount) =>
{
Console.WriteLine($"Tentativa {retryCount} falhou: {exception.Message}");
});
// Executa com a política
return await retryPolicy.ExecuteAsync(async () =>
{
var response = await _httpClient.GetAsync($"/api/produtos/{id}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Produto>();
});
}
}
2. Wait and Retry com Backoff Exponencial
Para cenários mais complexos, podemos usar wait-and-retry com intervalos crescentes:
public class NotaFiscalService
{
private readonly HttpClient _httpClient;
private readonly ILogger<NotaFiscalService> _logger;
public async Task<NotaFiscalResponse> EmitirNotaAsync(NotaFiscalRequest request)
{
var retryPolicy = Policy
.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(
retryCount: 4,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
var statusCode = outcome.Result?.StatusCode;
_logger.LogWarning(
"Tentativa {RetryCount} após {Timespan}s. StatusCode: {StatusCode}",
retryCount, timespan.TotalSeconds, statusCode);
});
var response = await retryPolicy.ExecuteAsync(async () =>
await _httpClient.PostAsJsonAsync("/api/nfe/emitir", request)
);
return await response.Content.ReadFromJsonAsync<NotaFiscalResponse>();
}
}
3. Renovação Automática de Token com Retry
Um cenário muito comum é renovar tokens JWT automaticamente quando expiram:
public class ApiClientService
{
private readonly HttpClient _httpClient;
private readonly ITokenService _tokenService;
private string _currentToken;
public async Task<T> ExecutarRequisicaoAsync<T>(string endpoint)
{
var retryPolicy = Policy<HttpResponseMessage>
.HandleResult(r => r.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(
retryCount: 3,
onRetryAsync: async (outcome, retryCount, context) =>
{
Console.WriteLine($"Token expirado. Renovando... Tentativa {retryCount}/3");
// Renova o token
_currentToken = await _tokenService.RenovarTokenAsync();
// Atualiza o header de autorização
_httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _currentToken);
});
var response = await retryPolicy.ExecuteAsync(async () =>
{
var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _currentToken);
return await _httpClient.SendAsync(request);
});
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<T>();
}
}
Boas Práticas
Escolha as políticas adequadas: Nem toda operação precisa de retry. Operações não-idempotentes devem ser tratadas com cuidado.
Configure timeouts apropriados: Evite que retries demorem muito tempo.
Use Circuit Breaker para dependências externas: Proteja sua aplicação de serviços instáveis.
Monitore e registre logs: Acompanhe quantos retries estão ocorrendo.
Conclusão
O Polly é uma ferramenta poderosa para adicionar resiliência às suas aplicações .NET. Com apenas algumas linhas de código, você pode tornar sua aplicação muito mais robusta e capaz de lidar com falhas transitórias de forma elegante.
A implementação de políticas de resiliência não é apenas uma boa prática - é essencial em arquiteturas distribuídas modernas. Com o Polly, você tem um kit de ferramenta completo para implementar padrões de resiliência de forma simples e manutenível.
Espero que este artigo tenha sido útil para entender como implementar resiliência em suas aplicações .NET 8!
Top comments (0)