As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Padrão de disjuntor
Intenção
O padrão do disjuntor pode impedir que um serviço de chamada repita uma chamada para outro serviço (chamador) quando a chamada já causou repetidos tempos limite ou falhas. O padrão também é usado para detectar quando o serviço do destinatário está funcionando novamente.
Motivação
Quando vários microsserviços colaboram para lidar com solicitações, um ou mais serviços podem ficar indisponíveis ou apresentar alta latência. Quando aplicativos complexos usam microsserviços, uma interrupção em um microsserviço pode levar à falha do aplicativo. Os microsserviços se comunicam por meio de chamadas de procedimentos remotos, e erros transitórios podem ocorrer na conectividade da rede, causando falhas. (Os erros transitórios podem ser tratados usando o padrão de repetição com recuo.) Durante a execução síncrona, a cascata de tempos limite ou falhas pode causar uma experiência ruim para o usuário.
No entanto, em algumas situações, as falhas podem levar mais tempo para serem resolvidas, por exemplo, quando o serviço do destinatário está inativo ou uma contenção no banco de dados resulta em tempos limite. Nesses casos, se o serviço de chamada repetir as chamadas repetidamente, essas novas tentativas poderão resultar em contenção na rede e no consumo do pool de threads do banco de dados. Além disso, se vários usuários repetirem o aplicativo repetidamente, isso piorará o problema e poderá causar degradação do desempenho de todo o aplicativo.
O padrão do disjuntor foi popularizado por Michael Nygard em seu livro Release It (Nygard 2018). Esse padrão de design pode impedir que um serviço de chamada repita uma chamada de serviço que já tenha causado repetidos tempos limite ou falhas. Ele também pode detectar quando o serviço do chamador está funcionando novamente.
Os objetos do disjuntor funcionam como disjuntores elétricos que interrompem automaticamente a corrente quando há uma anormalidade no circuito. Os disjuntores elétricos desligam ou desligam o fluxo da corrente quando há uma falha. Da mesma forma, o objeto do disjuntor está situado entre o chamador e o serviço do chamador e dispara se o chamador não estiver disponível.
As falácias da computação distribuída
Durante uma interrupção na rede, os aplicativos podem esperar por uma resposta indefinidamente e consumir continuamente os recursos do aplicativo. Deixar de repetir as operações quando a rede estiver disponível também pode levar à degradação do aplicativo. Se API as chamadas para um banco de dados ou um serviço externo expirarem devido a problemas de rede, chamadas repetidas sem disjuntor podem afetar o custo e o desempenho.
Aplicabilidade
Use esse padrão quando:
-
O serviço de chamadas faz uma chamada que provavelmente falhará.
-
A alta latência exibida pelo serviço do destinatário (por exemplo, quando as conexões do banco de dados são lentas) causa tempos limite no serviço do destinatário.
-
O serviço do chamador faz uma chamada síncrona, mas o serviço do chamador não está disponível ou apresenta alta latência.
Problemas e considerações
-
Implementação independente do serviço: para evitar o excesso de código, recomendamos que você implemente o objeto disjuntor de forma independente e orientada por microsserviços. API
-
Fechamento do circuito pelo chamador: Quando o chamador se recupera de um problema ou falha de desempenho, ele pode atualizar o status do circuito para.
CLOSED
Essa é uma extensão do padrão do disjuntor e pode ser implementada se seu objetivo de tempo de recuperação (RTO) exigir isso. -
Chamadas multiencadeadas: o valor do tempo limite de expiração é definido como o período de tempo em que o circuito permanece desligado antes que as chamadas sejam roteadas novamente para verificar a disponibilidade do serviço. Quando o serviço do destinatário é chamado em vários segmentos, a primeira chamada que falhou define o valor do tempo limite de expiração. Sua implementação deve garantir que as chamadas subsequentes não alterem o tempo limite de expiração indefinidamente.
-
Forçar a abertura ou o fechamento do circuito: os administradores do sistema devem ter a capacidade de abrir ou fechar um circuito. Isso pode ser feito atualizando o valor do tempo limite de expiração na tabela do banco de dados.
-
Observabilidade: O aplicativo deve ter o registro configurado para identificar as chamadas que falham quando o disjuntor está aberto.
Implementação
Arquitetura de alto nível
No exemplo a seguir, o chamador é o serviço de pedidos e o destinatário é o serviço de pagamento.
Quando não há falhas, o serviço de pedidos encaminha todas as chamadas para o serviço de pagamento pelo disjuntor, conforme mostra o diagrama a seguir.
Se o serviço de pagamento atingir o tempo limite, o disjuntor poderá detectar o tempo limite e rastrear a falha.
Se os tempos limite excederem um limite especificado, o aplicativo abrirá o circuito. Quando o circuito está aberto, o objeto do disjuntor não encaminha as chamadas para o serviço de pagamento. Ele retorna uma falha imediata quando o serviço de pedidos liga para o serviço de pagamento.
O objeto do disjuntor tenta periodicamente verificar se as chamadas para o serviço de pagamento foram bem-sucedidas.
Quando a chamada para o serviço de pagamento é bem-sucedida, o circuito é fechado e todas as chamadas adicionais são encaminhadas para o serviço de pagamento novamente.
Implementação usando AWS serviços
A solução de amostra usa fluxos de trabalho expressos AWS Step Functions
A solução também usa uma tabela do Amazon DynamoDB
Quando um serviço deseja chamar outro serviço, ele inicia o fluxo de trabalho com o nome do serviço do destinatário. O fluxo de trabalho obtém o status do disjuntor na tabela do CircuitStatus
DynamoDB, que armazena os serviços atualmente degradados. Se CircuitStatus
contiver um registro não expirado do receptor, o circuito está aberto. O fluxo de trabalho do Step Functions retorna uma falha imediata e sai com um FAIL
estado.
Se a CircuitStatus
tabela não contiver um registro para o destinatário ou contiver um registro expirado, o serviço estará operacional. A ExecuteLambda
etapa na definição da máquina de estado chama a função Lambda que é enviada por meio de um valor de parâmetro. Se a chamada for bem-sucedida, o fluxo de trabalho do Step Functions será encerrado com um SUCCESS
estado.
Se a chamada de serviço falhar ou ocorrer um tempo limite, o aplicativo tentará novamente com um recuo exponencial por um número definido de vezes. Se a chamada de serviço falhar após as novas tentativas, o fluxo de trabalho insere um registro na CircuitStatus
tabela do serviço com o anExpiryTimeStamp
, e o fluxo de trabalho sai com um estado. FAIL
As chamadas subsequentes para o mesmo serviço retornam uma falha imediata, desde que o disjuntor esteja aberto. A Get Circuit Status
etapa na definição da máquina de estado verifica a disponibilidade do serviço com base no ExpiryTimeStamp
valor. Os itens expirados são excluídos da CircuitStatus
tabela usando o recurso time to live () do DynamoDB. TTL
Código de exemplo
O código a seguir usa a função GetCircuitStatus
Lambda para verificar o status do disjuntor.
var serviceDetails = _dbContext.QueryAsync<CircuitBreaker>(serviceName, QueryOperator.GreaterThan, new List<object> {currentTimeStamp}).GetRemainingAsync(); if (serviceDetails.Result.Count > 0) { functionData.CircuitStatus = serviceDetails.Result[0].CircuitStatus; } else { functionData.CircuitStatus = ""; }
O código a seguir mostra as declarações da Amazon States Language no fluxo de trabalho Step Functions.
"Is Circuit Closed": { "Type": "Choice", "Choices": [ { "Variable": "$.CircuitStatus", "StringEquals": "OPEN", "Next": "Circuit Open" }, { "Variable": "$.CircuitStatus", "StringEquals": "", "Next": "Execute Lambda" } ] }, "Circuit Open": { "Type": "Fail" }
GitHub repositório
Para uma implementação completa da arquitetura de amostra desse padrão, consulte o GitHub repositório em https://github.com/aws-samples/circuit-breaker-netcore-blog