Circuit Breaker — Software à prova de falhas

Lucas Trindade
Tableless
Published in
5 min readJan 8, 2020
ponte móvel
Ponte móvel

1 — Historicamente o desenvolvimento de software é baseado no lema dividir para conquistar.

2 — A forma moderna de desenvolver software nos diz que o mesmo deve ser distribuído e de alta disponibilidade.

Por que escolhi essas duas frases, aparentemente desconexas, para iniciar o artigo? As duas afirmações apontam para uma mesma solução: microserviços.

O foco deste artigo não é discorrer sobre o que é um microserviço, mas sim como lidar com um problema que esta arquitetura gera. Como toda nova solução, essa forma de trabalhar traz consigo inúmeros benefícios, e também novos desafios: como resistir às falhas de comunicação entre os serviços que eventualmente(sempre) acontecem?

Para nossa sorte, outras pessoas já passaram pelo mesmo problema, e inclusive criaram um padrão de projeto especialmente para isso!

Circuit Breaker

Circuit breaker é um design pattern simples e poderoso, criado especialmente para evitar comportamentos inesperados, e planejar como lidar com todas as falhas que envolvam terceiros no qual sua aplicação possua alguma dependência.

A ideia é identificar interações problemáticas e tomar uma medida mitigadora sobre isso, seja ela evitar que o software tente continuamente se comunicar com o serviço problemático, buscar alternativas em cache, e até exibir uma mensagem amigável ao usuário sobre a instabilidade, sem comprometer outras partes da aplicação.

Conceitual

O pattern gira em torno de atribuir estados para sua interação, tendo comportamentos diferentes de acordo com estado atual. Entenda interação como, por exemplo, uma requisição para um serviço A por parte do serviço B. Na prática, iremos até atribuir nomes para a interação, pois precisaremos guardar algumas informações, sendo o estado uma delas.
A ideia de estados é a mesma de uma ponte móvel, essas que se levantam para passagem de navios (imagem no topo do artigo): quando ela está ABERTA, nenhum veículo passa por ela, quando está FECHADA, o tráfego é liberado.
Circuitos operam da mesma forma para controlar o fluxo de comunicação:

— Enquanto o circuito está FECHADO, a comunicação ocorre naturalmente
— Após um número X de tentativas sem sucesso, o circuito passa para o estado ABERTO, e as comunicações seguintes cairão no fluxo alternativo escolhido, seja ela buscar informações em outra fonte, utilizar um sistema de filas ou exibir uma mensagem de erro.

Há ainda um terceiro estado chamado SEMIABERTO ou MEIO ABERTO. Nele, após o circuito estar aberto, a aplicação tenta novamente se comunicar com o serviço problemático a fim de verificar se o mesmo já se recuperou da instabilidade. Caso obtenha sucesso na comunicação, o circuito passa para o estado FECHADO. Em caso de falha, o circuito novamente fica como ABERTO, e após um determinado tempo ficará MEIO ABERTO para efetuar uma nova verificação.
Esse ciclo se repete até a recuperação do terceiro.

Prático

Vamos agora transformar em código o conceito exposto acima. Basicamente a ideia é monitorar um bloco de código esperando por falhas. Quando o número de falhas chegar a uma quantidade configurada, o circuito é aberto, e por um período de tempo também configurado será executada alguma ação customizada.

Este pattern não tem uma única maneira correta de ser implementada, então resolvi não desenvolver um código para este artigo, mas expor dois pacotes no github que foram do meu agrado e possuem abordagens diferentes quanto à implementação.

PHP Circuit Breaker

Utilização da classe CircuitBreaker do pacote

Uma função anônima foi criada para executar o trecho de código que faz a comunicação. Em caso de falha, o método call abre o circuito e dispara uma exceção.
Em uma segunda tentativa de conexão, o método irá disparar exceção sem tentar novamente efetuar a comunicação.

Por dentro do método call conseguiremos entender a lógica do circuito:

Método call da classe CircuitBreaker

Na linha 18 é verificado se o circuito está aberto. Em caso positivo, aumenta-se o contador de tentativas com falha, e o processamento é interrompido.

Se chegou na linha 27 significa que o circuito está fechado ou meio aberto. O callback é executado (callback é a função anônima passada como parâmetro na chamada do método) e as verificações de sucesso e falha ocorrem, para decidir se o circuito será aberto ou seguirá fechado.

Laravel Circuit Breaker

Este pacote foi criado pensando no framework PHP Laravel, porém coloquei no artigo apenas para destacar sua forma de implementação, já que caso seja do agrado, replicá-lo de forma agnóstica ao framework é muito simples.

A classe Circuit Breaker, neste exemplo, é um Facade, e expõe um método isAvailable para o client escolher a melhor forma de verificar se o circuito está ou não aberto, e assim implementar sua ação personalizada, no caso do exemplo acima, o envio do processamento para uma fila. A mesma ação personalizada é executada em caso de exceção no processamento do bloco de código — tentativa de efetuar o pagamento — , além de reportar ao CircuitBreaker que houve uma falha.
Neste pacote, as configurações sobre quantidade de tentativas para o circuito abrir, tempo para uma nova tentativa, etc., estão em um arquivo de configuração, e há a possibilidade de informar diferentes valores para cada circuito que se está lidando.

Nota-se que para verificar a disponibilidade, foi enviado o namespace da classe PaymentsGateway. Esta foi a forma que o criador do pacote escolheu para dar nome ao circuito, mas poderia ser uma string normalizada qualquer.

Existem várias soluções na internet onde a classe circuit breaker possui um método call, e dentro deste método uma requisição HTTP acontece, mas esta abordagem não me agradou, pois viola o Princípio da Responsabilidade Única, já que a classe deve cuidar apenas do que diz respeito ao circuito. Além disso, a classe fica presa com o protocolo, quando, na verdade, o pattern deve ser livre de implementações de arquitetura, portanto não deve estar amarrado à requisições HTTP apenas.

Para finalizar

Usei microserviços como maior exemplo neste artigo, mas circuitbreaker pode ser aplicado à qualquer processo que seja propenso à falhas, e uma ação mitigadora possa ser executada, ainda que essa ação seja apenas dar um feedback de instabilidade ao usuário.

Há um artigo (em inglês) muito legal publicado pela Netflix sobre como eles utilizam e tiram proveito da implementação do pattern em suas APIs, vale a leitura. Segue o link: https://medium.com/netflix-techblog/making-the-netflix-api-more-resilient-a8ec62159c2d

Abraços, e até a próxima!

--

--