Tratamento de Falhas com .NET + Polly: implementando o padrão Circuit Breaker
A utilização da biblioteca Polly para a implementação de um tratamento de falhas mais inteligente em .NET já foi abordada diversas vezes neste blog:
.NET 5 + JWT + Polly + Refit: consumindo APIs seguras com simplicidade e resiliência
.NET 5 + Polly: melhorando o tratamento de falhas em exemplos práticos
Em uma live recente no Canal .NET retomei este assunto, trazendo desta vez um exemplo de implementação do pattern conhecido como Circuit Breaker:
Bastante frequente em arquiteturas baseadas em Microsserviços, este padrão envolve o uso de um objeto também chamado de Circuit Breaker:
- O Circuit Breaker inicia com o status Closed (Fechado), sendo empregado na execução de código de forma protegida e monitorando eventuais falhas que possam ocorrer;
- Quando o número de falhas atingir um certo limite dentro de um intervalo previamente definido de tempo o Circuit Breaker passará do status Closed para Aberto (Open). Neste momento tentativas de execução de instruções com o objeto que representa o circuito resultarão sempre em erro;
- Passado o período de tempo já mencionado, o Circuit Breaker entra então no status de Half-Open (Semiaberto), em que uma tentativa de execução será feita a fim de determinar se o circuito retorna para o status Closed (o esperado para situações normais).
O diagrama a seguir sumariza estas mudanças de estado:
E como o uso de Polly em .NET se encaixa em tudo isso?
Uma das opções disponibilizadas por Polly para a implementação do pattern Circuit Breaker é o tipo AsyncCircuitBreakerPolicy (namespace Polly.CircuitBreaker).
O exemplo que utilizei na live mencionada anteriormente já foi disponibilizado no GitHub (recomendo fortemente que você assista ao trecho que detalha a implementação do padrão Circuit Breaker):
https://github.com/renatogroffe/DotNet5-Worker-Polly-CircuitBreaker_ConsumoAPIContagem
Este repositório é formado por uma API REST (APIContagem), além de um Worker Service (WorkerConsumoAPIContagem) que consome esta API e no qual se definiu um Circuit Breaker com Polly.
Em WorkerConsumoAPIContagem foi implementada a classe estática CircuitBreaker:
- Com CreatePolicy teremos a criação de uma nova Policy baseada no tipo AsyncCircuitBreakerPolicy, sendo indicados no método CircuitBreakerAsync o número máximo de exceções antes que o circuito se feche (5 ao todo), o tempo em que o mesmo permanecerá em aberto (10 segundos) e o código para cada um dos estados possíveis (parâmetros onBreak, onReset e onHalfOpen);
- O método ShowCircuitState exibirá informações sobre cada passagem de estado.
O objeto que representa o Circuit Breaker (e gerado através de uma chamada a CreatePolicy em CircuitBreaker) será resolvido via injeção de dependências. A instância em questão foi configurada na classe Program, através do uso do método AddSingleton:
Na classe Worker teremos finalmente a utilização do Circuit Breaker:
- A dependência para este tipo será resolvida no construtor de Worker (linhas 22 e 26);
- A instrução a ser executado de forma protegida pelo Circuit Breaker está nas linhas 40 e 41.
Na imagem a seguir é possível observar a execução do projeto de testes. Embora falhas tenham ocorrido, o Circuit Breaker não chega a ser aberto (o status Closed permanece) e a aplicação consegue se recuperar dos problemas nesta simulação:
Após sucessivas falhas teremos a abertura do circuito (status Open), com tentativas de execução gerando exceções que indicam tal status:
Por fim, temos um exemplo de novo fechamento do circuito após o problema com a API de testes ser corrigido e a comunicação entre as aplicações restabelecida (com a passagem dos status de Open para Half Open e depois para Closed):