Back-End

18 mar, 2019

Aumente a resiliência do seu software com o Circuit Breaker

Publicidade

Este artigo foi publicado originalmente em: https://www.concrete.com.br/2019/03/08/circuit-breaker/

***

Imagine uma aplicação desenvolvida usando REST com microsserviços. Agora pense em um cliente que consome um serviço que demora pra responder.

A partir daí, vários outros clientes fazem chamadas ao serviço e ele não responde, ou ainda a rede do servidor está congestionada, ou o banco de dados apresenta algum problema.

Pior do que isso: pense em um caso em que seu software está sofrendo um ataque de negação de serviço. Pensou? Enquanto isso, o que acontece com os recursos do servidor? Faz sentido as chamadas subsequentes realizarem requisições a esse serviço?

Bom, hoje vou apresentar neste artigo uma solução muito boa para responder a essas perguntas.

Antes disso, porém, vamos entender um pouco sobre o que é resiliência. Se procurarmos no dicionário, temos:

  • “Na física, resiliência é a propriedade que alguns corpos apresentam de retornar à forma original após terem sido submetidos a uma deformação elástica.”

No universo dos softwares, porém, “corpo” pode ser substituído por API. O próprio software ou algum serviço, enquanto “retornar à forma original após terem sido submetidos a uma deformação elástica” é basicamente a capacidade de se recuperar das falhas – qualquer uma delas.

Circuit Breaker

O Circuit Breaker é um padrão que propõe respostas às perguntas que fizemos lá no início. O objetivo dele é acompanhar todas as requisições a fim de monitorar as falhas.

Quando os defeitos atingem um certo limite, o Circuit Breaker impede que a chamada real seja executada. Depois de um determinado tempo, ele faz novamente a chamada real e, se ela responder, todas as novas chamadas estão liberadas.

Se não, a ferramenta continua a impedir a chamada real e segue esse fluxo até que a chamada real consiga responder corretamente. Ficou confuso? Calma que eu te explico melhor.

Pense no Circuit Breaker com um circuito elétrico, no qual existe a figura do disjuntor. Ele é projetado para suportar uma certa quantidade de corrente elétrica e, quando esse valor é atingido, interrompe o circuito para proteger todos os elementos que o compõem.

Assim que o problema for resolvido, o disjuntor pode ser rearmado para que o circuito continue funcionando. No caso do software, o intuito é o mesmo. Veja os status abaixo que vão te ajudar a entender melhor:

  • Closed – quando tudo está correndo bem: as requisições são passadas para os serviços e eles respondem normalmente. No momento em que o limite de falhas é atingido, o circuito desarma e passa para o estado Open
  • Open: esse é o estado em que um circuito elétrico não deixa a corrente passar. Transferindo o conceito para os softwares, é a partir desse momento que a chamada remota não será executada
  • Half Open: depois de um período de tempo, o circuito realiza uma requisição para saber se o serviço está ok

Caso ele continue com problemas, o circuito é desarmado novamente e retorna para o status Open. Se tudo ocorrer bem, o status volta para Closed.

Mão na massa

Até agora nós vimos os conceitos, mas isso funciona mesmo ou é só teoria? Funciona! A prática vem agora!

Já que o Circuit Breaker é um padrão, ele não está associado a nenhuma linguagem. Dessa forma, podemos escolher qualquer uma delas para implementá-lo. Neste artigo vou usar o C#.

Também vamos usar a biblioteca Polly para ilustrar o nosso exemplo. Com ela, podemos definir políticas de resiliência, tais como o Circuit Breaker, timeout ou políticas de fallback. A Polly utiliza a classe Policy para definir as suas políticas e, nesta classe, já temos o Circuit Breaker implementado.

Para usarmos, precisamos definir o tipo da exceção que o Circuit Breaker vai capturar, quantas exceções ele vai aceitar antes de abrir o circuito e a duração que o circuito deve ficar aberto antes de reiniciar.

Depois disso, como usar a nova política? Basta executá-la com um cliente HTTP e realizar uma chamada:

Depois de quantas tentativas o circuito passa para o estado Open? E a chamada real vai ser feita quando? Para isso, precisamos criar outra política. Nela, assim que uma exceção do tipo HttpRequestException for lançada, o sistema vai esperar dois segundos e repetir esse processo três vezes.

E qual a vantagem disso? Ninguém precisa “rearmar” o software. A cada dois segundos ele mesmo faz três tentativas para a chamada real para saber se está tudo ok. Além disso, essa estratégia ajuda a diminuir os gastos de recursos do servidor, tais como memória e processamento.

Para executar a nova política criada acima, basta chamar o método ExecuteAsync da mesma maneira que foi feito com a regra de Circuit Breaker.

E aí você me pergunta

  • “Mas eu tenho que fazer a mesma chamada para cada política? Não tem como eu fazer tudo junto?”

E eu te respondo: é claro que tem!

A Polly tem um método que faz isso pra gente, o WrapAsync. Desse jeito, podemos implementar um método que cria as políticas e depois executamos todas de uma vez só.

E se o serviço responder com um timeout? Você consegue logar para ver o que aconteceu! A Polly possui também um método para isso.

Vamos instanciar mais uma política e definir o número de segundos antes do timeout, o tipo de estratégia e uma ação que será executada quando o timeout ocorrer.

Mas essa política só ajuda com os logs? Não! Até esse ponto, o Circuit Breaker vai proteger a chamada real de ser executada enquanto alguma intermitência estiver ocorrendo.

A cada intervalo de tempo, o próprio software verifica sozinho se a falha já foi corrigida, porém, a cada chamada, os recursos do servidor continuam a ser consumidos. Com essa regra conseguimos impedir o gasto excessivo desses recursos.

Tipos de estratégia

Temos dois tipos de estratégia nesse contexto todo: a pessimista e a otimista. A otimista usa um CancellationToken de tempo limite para cancelar a operação executada, enquanto a pessimista existe para aqueles momentos em nenhum componente utilizado ofereça uma forma de fornecer um tempo limite para cancelamento. Se você se interessou por essas técnicas, existe um artigo bem completo sobre isso neste link.

Conclusão

O legal do Circuit Breaker é ser um padrão e não estar preso a nenhuma linguagem de programação. Além disso, há vários tutoriais ensinando a implementá-lo, caso queiram assimilar com mais apreço o conceito.

Depois do conteúdo dominado, a biblioteca Polly, no caso do C#, já nos poupa um bom tempo com todas aquelas funcionalidades implementadas. Assim, podemos gastar mais tempo nas regras de negócio e deixar o trabalho de configuração pra ela.

Se você ficou curioso para ver o Circuit Breaker numa aplicação real, não deixe de consultar o exemplo criado neste artigo aqui.

E abaixo vou deixar uma lista de mais referências, caso você se interesse mais. Ficou alguma dúvida ou tem algo a dizer? Aproveite os campos abaixo.

Até a próxima!

Referências