Partilhar via


Padrão Circuit Breaker

O Padrão Circuit Breaker ajuda a lidar com falhas que podem levar tempos variados para se recuperarem ao se ligar a uma aplicação a um serviço ou recurso remoto. Um disjuntor bloqueia temporariamente o acesso a um serviço defeituoso depois de detetar falhas. Esta ação evita repetidas tentativas malsucedidas para que o sistema possa recuperar de forma eficaz. Esse padrão pode melhorar a estabilidade e a resiliência de um aplicativo.

Contexto e problema

Em um ambiente distribuído, as chamadas para recursos e serviços remotos podem falhar devido a falhas transitórias. As falhas transitórias incluem recursos sobrecomprometidos ou temporariamente indisponíveis, conexões de rede lentas ou tempos limites. Essas falhas normalmente se corrigem após um curto período de tempo. Para ajudar a gerenciar essas falhas, você deve projetar um aplicativo em nuvem para usar uma estratégia, como o padrão Repetir.

Eventos imprevistos podem criar falhas que levam mais tempo para serem corrigidas. Essas falhas podem variar em gravidade, desde uma perda parcial de conectividade até uma falha completa do serviço. Nessas situações, um aplicativo não deve repetir continuamente uma operação que provavelmente não terá êxito. Em vez disso, o aplicativo deve reconhecer rapidamente a operação com falha e lidar com a falha de acordo.

Se um serviço estiver ocupado, uma falha em uma parte do sistema pode levar a falhas em cascata. Por exemplo, você pode configurar uma operação que invoca um serviço para implementar um tempo limite. Se o serviço não responder dentro desse período, a operação responderá com uma mensagem de falha.

No entanto, essa estratégia pode bloquear solicitações simultâneas para a mesma operação até que o período de tempo limite expire. Essas solicitações bloqueadas podem conter recursos críticos do sistema, como memória, threads e conexões de banco de dados. Esse problema pode esgotar recursos, o que pode falhar outras partes não relacionadas do sistema que precisam usar os mesmos recursos.

Nessas situações, uma operação deve falhar imediatamente e só tentar invocar o serviço se for provável que seja bem-sucedida. Para resolver esse problema, defina um tempo limite mais curto. Mas certifique-se de que o tempo limite é longo o suficiente para que a operação seja bem-sucedida na maioria das vezes.

Solução

O padrão de disjuntor ajuda a evitar que um aplicativo tente executar repetidamente uma operação que provavelmente falhará. Esse padrão permite que o aplicativo continue em execução sem esperar que a falha seja corrigida ou desperdiçar ciclos de CPU ao determinar que a falha é persistente. O padrão de disjuntor também permite que um aplicativo detete quando a falha é resolvida. Se a falha for resolvida, o aplicativo pode tentar invocar a operação novamente.

Observação

O padrão Circuit Breaker serve a um propósito diferente do padrão Retry. O padrão Retry permite que um aplicativo tente novamente uma operação com a expectativa de que ela eventualmente seja bem-sucedida. O padrão Disjuntor impede que um aplicativo execute uma operação que provavelmente falhará. Uma aplicação pode combinar estes dois padrões ao utilizar o padrão de Repetição para invocar uma operação através de um disjuntor automático. No entanto, a lógica de repetição deve ser sensível a quaisquer exceções que o disjuntor retorne e deve interromper as tentativas de repetição se o disjuntor indicar que uma falha não é transitória.

Um disjuntor automático atua como proxy para operações suscetíveis de falhar. O proxy deve monitorar o número de falhas recentes e usar essas informações para decidir se permite que a operação prossiga ou se retorna uma exceção imediatamente.

Você pode implementar o proxy como uma máquina de estado que inclui os seguintes estados. Estes estados imitam a funcionalidade de um disjuntor elétrico:

  • Fechado: A solicitação da aplicação é encaminhada para a operação. O proxy mantém uma contagem do número de falhas recentes. Se a chamada para a operação não for bem-sucedida, o proxy incrementará essa contagem. Se o número de falhas recentes exceder um limite especificado dentro de um determinado período de tempo, o proxy será colocado no estado Aberto e iniciará um temporizador de tempo limite. Quando o temporizador expira, o proxy é colocado no estado Half-Open .

    Observação

    Durante o tempo limite, o sistema tenta corrigir o problema que causou a falha antes de permitir que o aplicativo tente a operação novamente.

  • Aberto: A solicitação do aplicativo falha imediatamente e uma exceção é retornada ao aplicativo.

  • Semi-Aberto: Um número limitado de solicitações do aplicativo pode passar e invocar a operação. Se essas solicitações forem bem-sucedidas, o disjuntor assume que a falha que causou a falha foi corrigida e o disjuntor muda para o estado Fechado . O contador de falhas é redefinido. Se alguma solicitação falhar, o disjuntor assume que a falha ainda está presente, então ele reverte para o estado Aberto . Ele reinicia o temporizador de tempo limite para que o sistema possa se recuperar da falha.

    Observação

    O estado Semiaberto ajuda a evitar que um serviço em recuperação seja repentinamente inundado de solicitações. À medida que um serviço se recupera, ele pode ser capaz de suportar um volume limitado de solicitações até que a recuperação seja concluída. Mas, enquanto a recuperação está em andamento, uma enxurrada de trabalho pode fazer com que o serviço exceda o tempo limite ou falhe novamente.

O diagrama seguinte mostra as operações do contador para cada estado.

Diagrama que mostra os estados do disjuntor.

O contador de falhas para o estado Fechado é baseado no tempo. Ele reinicia automaticamente em intervalos periódicos. Este design ajuda a evitar que o disjuntor entre no estado Aberto se ocorrer falhas ocasionais. O limite de falha aciona o estado Aberto somente quando um número especificado de falhas ocorre durante um intervalo especificado.

O contador de êxito para o estado Half-Open registra o número de tentativas bem-sucedidas de invocar a operação. O disjuntor reverte para o estado Fechado após um número especificado de invocações de operação consecutivas bem-sucedidas. Se alguma invocação falhar, o disjuntor entra imediatamente no estado Aberto e o contador de êxitos é redefinido na próxima vez que entrar no estado Semiaberto.

Observação

A recuperação do sistema é baseada em operações externas, como restaurar ou reiniciar um componente com falha ou reparar uma conexão de rede.

O padrão do Disjuntor Automático confere estabilidade enquanto o sistema recupera de uma falha e minimiza o impacto no desempenho. Pode ajudar a manter o tempo de resposta do sistema. Esse padrão rejeita rapidamente uma solicitação para uma operação que provavelmente falhará, em vez de esperar que a operação atinja o tempo limite ou nunca retorne. Se o disjuntor gerar um evento cada vez que ele mudar de estado, essas informações podem ajudar a monitorar a integridade do componente do sistema protegido ou alertar um administrador quando um disjuntor mudar para o estado Aberto .

Você pode personalizar e adaptar esse padrão a diferentes tipos de falhas. Por exemplo, você pode aplicar um temporizador de tempo limite crescente a um disjuntor. Você pode colocar o disjuntor no estado Aberto por alguns segundos inicialmente. Se a falha não for resolvida, aumente o tempo limite para alguns minutos e ajuste conforme necessário. Em alguns casos, em vez de retornar uma falha e gerar uma exceção, o estado Open pode retornar um valor padrão que é significativo para o aplicativo.

Observação

Tradicionalmente, os disjuntores dependiam de limites pré-configurados, como contagem de falhas e duração do tempo limite. Esta abordagem resultou num comportamento determinístico, mas por vezes subótimo.

Técnicas adaptativas que usam IA e aprendizado de máquina podem ajustar dinamicamente os limites com base em padrões de tráfego em tempo real, anomalias e taxas de falha históricas. Esta abordagem melhora a resiliência e a eficiência.

Problemas e considerações

Considere os seguintes fatores ao implementar esse padrão:

  • Tratamento de exceções: Um aplicativo que invoca uma operação por meio de um disjuntor deve ser capaz de lidar com as exceções se a operação não estiver disponível. O gerenciamento de exceções é baseado no aplicativo. Por exemplo, um aplicativo pode degradar temporariamente sua funcionalidade, invocar uma operação alternativa para tentar executar a mesma tarefa ou obter os mesmos dados, ou relatar a exceção ao usuário e pedir-lhe para tentar novamente mais tarde.

  • Tipos de exceções: As razões para uma falha de solicitação podem variar em gravidade. Por exemplo, uma solicitação pode falhar porque um serviço remoto falha e requer vários minutos para se recuperar, ou porque um serviço sobrecarregado causa um tempo limite. Um disjuntor pode ser capaz de examinar os tipos de exceções que ocorrem e ajustar sua estratégia com base na natureza dessas exceções. Por exemplo, pode exigir um número maior de exceções de tempo limite para acionar o disjuntor para o estado Aberto em comparação com o número de falhas causadas pelo serviço indisponível.

  • Monitorização: Um disjuntor deve fornecer observabilidade clara em solicitações com falha e bem-sucedidas para que as equipes de operações possam avaliar a integridade do sistema. Use o rastreamento distribuído para visibilidade de ponta a ponta entre serviços.

  • Capacidade de recuperação: Você deve configurar o disjuntor para corresponder ao provável padrão de recuperação da operação que ele protege. Por exemplo, se o disjuntor permanecer no estado Aberto por um longo período, ele pode gerar exceções mesmo se o motivo da falha for resolvido. Da mesma forma, um disjuntor pode flutuar e reduzir os tempos de resposta das aplicações se mudar do estado Aberto para o estado Semiaberto muito rapidamente.

  • Teste de operações com falha: No estado Aberto , em vez de usar um temporizador para determinar quando alternar para o estado Semiaberto , um disjuntor pode executar periodicamente ping no serviço remoto ou recurso para determinar se ele está disponível. Esse ping pode tentar invocar uma operação com falha anterior ou usar uma operação especial de verificação de integridade fornecida pelo serviço remoto. Para obter mais informações, consulte o padrão de monitorização de endpoint de saúde .

  • Substituição manual: Se o tempo de recuperação de uma operação com falha for extremamente variável, deve-se fornecer uma opção de reinicialização manual que permita a um administrador fechar um disjuntor e reinicializar o contador de falhas. Da mesma forma, um administrador pode forçar um disjuntor para o estado Aberto e reiniciar o temporizador de tempo limite se a operação protegida estiver temporariamente indisponível.

  • Simultaneidade: Um grande número de instâncias simultâneas de um aplicativo pode acessar o mesmo disjuntor. A implementação não deve bloquear pedidos simultâneos ou adicionar uma carga excessiva a cada chamada a uma operação.

  • Diferenciação de recursos: Tenha cuidado ao usar um único disjuntor para um tipo de recurso se houver vários provedores independentes subjacentes. Por exemplo, em um armazenamento de dados que contém vários fragmentos, um fragmento pode estar totalmente acessível enquanto outro enfrenta um problema temporário. Se as respostas de erro nesses cenários forem mescladas, um aplicativo poderá tentar acessar alguns fragmentos, mesmo quando for provável uma falha. E o acesso a outros fragmentos pode ser bloqueado, mesmo que seja provável que tenha sucesso.

  • Desarme acelerado do disjuntor: Às vezes, uma resposta de falha pode conter informações suficientes para que o disjuntor dispare imediatamente e permaneça disparado por um período mínimo de tempo. Por exemplo, a resposta de erro de um recurso compartilhado que está sobrecarregado pode indicar que o aplicativo deve tentar novamente em alguns minutos, em vez de tentar novamente.

  • Implantações multirregionais: Você pode projetar um disjuntor para implantações de região única ou multirregião. Para projetar implantações em várias regiões, use balanceadores de carga globais ou estratégias personalizadas de quebra de circuitos com reconhecimento de região que ajudam a garantir failover controlado, otimização de latência e conformidade normativa.

  • Disjuntores de malha de serviço: Você pode implementar disjuntores na camada de aplicação ou como um recurso transversal e abstrato. Por exemplo, as malhas de serviço geralmente suportam a quebra de circuito como um sidecar ou como uma capacidade autónoma sem modificar o código da aplicação.

    Observação

    Um serviço pode retornar HTTP 429 (muitas solicitações) se estiver limitando o cliente ou HTTP 503 (serviço indisponível) se o serviço não estiver disponível. A resposta pode incluir outras informações, como a duração prevista do atraso.

  • Falha na repetição da solicitação: No estado Aberto , em vez de simplesmente falhar rapidamente, um disjuntor também pode registrar os detalhes de cada solicitação em um diário e providenciar para que essas solicitações sejam reproduzidas quando o recurso ou serviço remoto estiver disponível.

  • Tempos de espera inadequados para serviços externos: Um disjuntor pode não proteger totalmente as aplicações contra falhas em serviços externos com longos períodos de tempo de espera. Se o tempo limite for muito longo, uma rosca que executa um disjuntor pode ser bloqueada por um longo período antes que o disjuntor indique que a operação falhou. Durante esse tempo, muitas outras instâncias de aplicações também podem tentar invocar o serviço por meio do disjuntor e bloquear várias threads antes que todas falhem.

  • Adaptabilidade à diversificação computacional: Os disjuntores devem levar em conta diferentes ambientes de computação, desde cargas de trabalho sem servidor até cargas de trabalho em contêineres, onde fatores como arranques a frio e escalabilidade afetam o tratamento de falhas. As abordagens adaptativas podem ajustar dinamicamente as estratégias com base no tipo de computação, o que ajuda a garantir a resiliência em arquiteturas heterogêneas.

Quando usar este padrão

Utilize este padrão quando:

  • Você deseja evitar falhas em cascata interrompendo chamadas de serviço remoto excessivas ou solicitações de acesso a um recurso compartilhado se essas operações provavelmente falharem.

  • Você deseja rotear o tráfego de forma inteligente com base em sinais de falha em tempo real para melhorar a resiliência de várias regiões.

  • Você deseja se proteger contra dependências lentas para que possa manter seus objetivos de nível de serviço e evitar a degradação do desempenho de serviços de alta latência.

  • Você deseja gerenciar problemas intermitentes de conectividade e reduzir falhas de solicitação em ambientes distribuídos.

Este padrão pode não ser adequado quando:

  • Você precisa gerenciar o acesso a recursos privados locais em um aplicativo, como estruturas de dados na memória. Neste ambiente, um disjuntor adiciona sobrecarga ao seu sistema.

  • Você precisa usá-lo como um substituto para lidar com exceções na lógica de negócios de seus aplicativos.

  • Algoritmos de repetição bem conhecidos são suficientes e suas dependências são projetadas para lidar com mecanismos de repetição. Nesse cenário, um disjuntor em seu aplicativo pode adicionar complexidade desnecessária ao seu sistema.

  • Esperar que um disjuntor seja reposto pode introduzir atrasos inaceitáveis.

  • Você tem uma arquitetura orientada por mensagens ou por eventos, pois essas arquiteturas muitas vezes encaminham mensagens com falha para uma fila de mensagens mortas para processamento manual ou adiado. Os mecanismos embutidos de isolamento e tentativa de repetição de falhas geralmente são suficientes.

  • A recuperação de falhas é gerenciada no nível da infraestrutura ou da plataforma, como com verificações de integridade em balanceadores de carga globais ou malhas de serviço.

Design da carga de trabalho

Avalie como utilizar o padrão Circuit Breaker no design de uma carga de trabalho para atingir os objetivos e princípios discutidos nos pilares do Azure Well-Architected Framework. A tabela a seguir fornece orientação sobre como esse padrão suporta as metas de cada pilar.

Pilar Como esse padrão suporta os objetivos do pilar
As decisões de projeto de confiabilidade ajudam sua carga de trabalho a se tornar resiliente ao mau funcionamento e garantem que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. Esse padrão ajuda a evitar que uma dependência com falha seja sobrecarregada. Use esse padrão para desencadear uma degradação normal na carga de trabalho. Acoplar disjuntores com recuperação automática para proporcionar autopreservação e autocura.

- RE:03 Análise do modo de falha
- Falhas transitórias
- RE:07 Autopreservação
A Eficiência de Desempenho ajuda sua carga de trabalho a atender às demandas de forma eficiente por meio de otimizações em escala, dados e código. Esse padrão evita a abordagem de tentar novamente em caso de erro, que pode levar ao uso excessivo de recursos durante a recuperação de dependências e pode sobrecarregar o desempenho numa dependência que está em processo de recuperação.

- PE:07 Código e infraestrutura
- PE:11 Respostas a questões em direto

Se este padrão introduzir compensações dentro de um pilar, considere-as em relação aos objetivos dos outros pilares.

Exemplo

Este exemplo implementa o padrão Circuit Breaker para ajudar a evitar a superação de quotas usando a camada gratuita vitalícia do Azure Cosmos DB. Essa camada é principalmente para dados não críticos e opera sob um plano de capacidade que aloca uma cota específica de unidades de recursos por segundo. Durante eventos sazonais, a procura pode exceder a capacidade disponibilizada, o que pode resultar em respostas 429.

Quando ocorrem picos de demanda, os alertas do Azure Monitor com limites dinâmicos detetam e notificam proativamente as equipes de operações e gerenciamento de que o banco de dados requer mais capacidade. Simultaneamente, um disjuntor que é ajustado usando padrões de erro históricos dispara para evitar falhas em cascata. Nesse estado, o aplicativo se degrada graciosamente retornando respostas padrão ou armazenadas em cache. O aplicativo informa os usuários da indisponibilidade temporária de certos dados, preservando a estabilidade geral do sistema.

Esta estratégia aumenta a resiliência que se alinha com a justificação do negócio. Ele controla picos de capacidade para que as equipes de carga de trabalho possam gerenciar aumentos de custos deliberadamente e manter a qualidade do serviço sem aumentar inesperadamente as despesas operacionais. Depois que a demanda diminui ou o aumento da capacidade é confirmado, o disjuntor é reiniciado e o aplicativo retorna à funcionalidade completa que se alinha com os objetivos técnicos e orçamentários.

Diagrama que mostra o Azure Cosmos DB e uma implementação de disjuntor no Serviço de Aplicativo do Azure.

O diagrama tem três seções primárias. A primeira seção contém dois ícones do navegador da Web. O primeiro ícone exibe uma interface de usuário totalmente funcional e o segundo ícone mostra uma experiência de usuário degradada que tem um aviso na tela para indicar o problema aos usuários. A segunda secção é encerrada dentro de um retângulo tracejado, que é dividido em dois grupos. O grupo superior inclui os recursos de carga de trabalho, o Serviço de Aplicativo e o Azure Cosmos DB. As setas de ambos os ícones do navegador da Web apontam para a instância do Serviço de Aplicativo, representando as solicitações de entrada do cliente. Além disso, as setas da instância do Serviço de Aplicativo apontam para o Azure Cosmos DB, que indicam interações de dados entre os serviços de aplicativo e o banco de dados. Outra seta faz um loop da instância do App Service de volta para si mesma, simbolizando o mecanismo de tempo limite do disjuntor. Esse loop significa que, quando uma resposta 429 Too Many Requests é detetada, o sistema volta a servir respostas em cache, degradando a experiência do usuário até que a situação se resolva. O grupo inferior desta secção foca-se na observabilidade e nos alertas. O Azure Monitor coleta dados dos recursos do Azure no grupo superior. O Azure Monitor também se conecta a um ícone de regra de alerta. A terceira seção mostra o fluxo de trabalho de escalabilidade que é acionado quando o alerta é gerado. Uma seta conecta o ícone de alerta aos aprovadores, o que indica que a notificação é enviada a eles para revisão. Outra seta indica desde os aprovadores até um console de desenvolvimento, o que representa o processo de aprovação para escalar o banco de dados. Finalmente, uma seta subsequente se estende do console de desenvolvimento para o Azure Cosmos DB, que representa a ação de dimensionar o banco de dados em resposta à condição de sobrecarga.

Baixe um arquivo Visio desta arquitetura.

Fluxo A: Estado fechado

  • O sistema opera normalmente e todas as solicitações chegam ao banco de dados sem retornar respostas 429 HTTP.

  • O disjuntor permanece fechado e nenhuma resposta padrão ou em cache é necessária.

Fluxo B: Estado aberto

  1. Quando o disjuntor recebe a primeira 429 resposta, ele viaja para um estado aberto .

  2. As solicitações subsequentes são imediatamente curto-circuitadas, o que retorna respostas padrão ou armazenadas em cache e informa os usuários sobre degradação temporária. O aplicativo está protegido contra mais sobrecarga.

  3. O Azure Monitor recebe logs e dados de telemetria e os avalia em relação a limites dinâmicos. Um alerta é acionado se as condições da regra de alerta forem atendidas.

  4. Um grupo de ação notifica proativamente a equipe de operações sobre a condição de sobrecarga.

  5. Após a aprovação da equipa de carga de trabalho, a equipa de operações pode aumentar a taxa de transferência provisionada para aliviar a sobrecarga ou adiar o dimensionamento se a carga diminuir naturalmente.

Fluxo C: estado Half-Open

  1. Após um tempo limite predefinido, o disjuntor entra em um estado de semi-abertura que permite um número limitado de solicitações de teste.

  2. Se essas solicitações de avaliação forem bem-sucedidas sem retornar 429 respostas, o disjuntor será redefinido para um estado Fechado e as operações normais serão restauradas para o Fluxo A. Se as falhas persistirem, o disjuntor reverte para o estado Aberto ou Fluxo B.

Componentes

  • O Serviço de Aplicativo do Azure hospeda o aplicativo Web que serve como o principal ponto de entrada para solicitações de clientes. O código do aplicativo implementa a lógica que impõe políticas de disjuntor e fornece respostas padrão ou armazenadas em cache quando o circuito está aberto. Essa arquitetura ajuda a evitar a sobrecarga nos sistemas downstream e a manter a experiência do usuário durante picos de demanda ou falhas.

  • O Azure Cosmos DB é um dos repositórios de dados da aplicação. Ele atende dados não críticos por meio do nível gratuito, que é ideal para pequenas cargas de trabalho de produção. O mecanismo de disjuntor ajuda a limitar o tráfego para o banco de dados durante períodos de alta demanda.

  • O Azure Monitor funciona como a solução de monitoramento centralizado. Ele agrega todos os logs de atividades para ajudar a garantir uma observabilidade abrangente de ponta a ponta. O Azure Monitor recebe logs e dados de telemetria do Serviço de Aplicativo e métricas-chave do Azure Cosmos DB (como o número de 429 respostas) para agregação e análise.

  • Alertas do Azure Monitor avaliam as regras de alerta contra os limites dinâmicos para identificar possíveis interrupções com base em dados históricos. Os alertas predefinidos notificam a equipe de operações quando os limites são violados.

    Às vezes, a equipe de carga de trabalho pode aprovar um aumento na taxa de transferência provisionada, mas a equipe de operações antecipa que o sistema pode se recuperar por conta própria porque a carga não é muito alta. Nestes casos, o tempo limite do disjuntor decorre naturalmente. Durante esse tempo, se as respostas 429 cessarem, o cálculo do limite deteta as interrupções prolongadas e as exclui do algoritmo de aprendizagem. Como resultado, da próxima vez que ocorrer uma sobrecarga, o limite irá aguardar por uma taxa de erro mais alta no Azure Cosmos DB, atrasando a notificação. Este ajuste permite que o disjuntor lide com o problema sem um alerta imediato, o que reduz o custo e melhora a eficiência operacional.

  • O padrão Reliable Web App aplica o padrão Circuit Breaker a aplicativos Web que convergem para a nuvem.

  • O padrão Repetir descreve como um aplicativo pode lidar com falhas temporárias antecipadas quando tenta se conectar a um serviço ou recurso de rede repetindo de forma transparente uma operação que falhou anteriormente.

  • O padrão Health Endpoint Monitoring descreve como um disjuntor pode testar a integridade de um serviço enviando uma solicitação para um ponto de extremidade exposto pelo serviço. O serviço deve retornar informações que indiquem seu status.