DEV Community

Cover image for Implementando Resiliência em Aplicações .NET 8 com Polly
Bruno Silva
Bruno Silva

Posted on

Implementando Resiliência em Aplicações .NET 8 com Polly

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
Enter fullscreen mode Exit fullscreen mode

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>();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

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>();
    }
}
Enter fullscreen mode Exit fullscreen mode

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>();
    }
}
Enter fullscreen mode Exit fullscreen mode

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!

dotnet #polly #resilience #microservices #braziliandevs

Top comments (0)