Processe falhas que possam demorar um período de tempo de recuperação variável ao ligar a um serviço ou recurso remoto. 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, como conexões de rede lentas, tempos limite ou recursos comprometidos em excesso ou temporariamente indisponíveis. Estas falhas costumam ser corrigidas de forma autónoma após um breve período de tempo, e uma aplicação na cloud robusta deve ser preparada para processá-las através de uma estratégia como o Padrão de repetição.
No entanto, pode também haver situações em que as falhas se devem a eventos imprevistos e podem demorar muito mais tempo a corrigir. Estas falhas podem variar em termos de gravidade, de uma perda parcial de conectividade à falha total de um serviço. Nessas situações, pode ser inútil para um aplicativo repetir continuamente uma operação que provavelmente não terá êxito e, em vez disso, o aplicativo deve aceitar rapidamente que a operação falhou e lidar com essa falha de acordo.
Adicionalmente, se um serviço estiver muito ocupado, a falha numa parte de um sistema poderá gerar falhas em cascata. Por exemplo, uma operação que invoca um serviço pode ser configurada para implementar um tempo limite e responder com uma mensagem de falha se o serviço não responder dentro desse período. No entanto, essa estratégia pode fazer com que muitas solicitações simultâneas para a mesma operação sejam bloqueadas até que o período de tempo limite expire. Estes pedidos bloqueados poderão conter recursos de sistema cruciais, como memória, threads, ligações de base de dados, etc. Assim, esses recursos podem se esgotar, causando falha de outras partes possivelmente não relacionadas do sistema que precisam usar os mesmos recursos. Nestas situações, é preferível que a operação falhe imediatamente e tente apenas invocar o serviço se tiver boas probabilidades de sucesso. Definir um tempo limite mais curto pode ajudar a resolver esse problema, mas o tempo limite não deve ser tão curto que a operação falhe na maioria das vezes, mesmo que a solicitação ao serviço acabe sendo bem-sucedida.
Solução
O padrão de disjuntor pode impedir que um aplicativo tente executar repetidamente uma operação que provavelmente falhará. Permitir que esta continue sem esperar que a falha seja corrigida ou desperdiçar ciclos de CPU enquanto determina que a falha é duradoura. O padrão do Disjuntor Automático também permite que uma aplicação detete se a falha foi resolvida. Se o problema aparentar estar resolvido, a aplicação pode tentar invocar a operação.
O objetivo do padrão do Disjuntor Automático é diferente do Padrão de repetição. O Padrão de repetição permite a uma aplicação repetir uma operação com a expectativa de que esta seja bem-sucedida. O padrão do Disjuntor Automático impede que uma aplicação efetue uma operação que é provável que falhe. 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 eventuais exceções devolvidas pelo disjuntor automático e abandonar tentativas de repetição se o disjuntor automático 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 que ocorreram e usar essas informações para decidir se permite que a operação prossiga ou retorna uma exceção imediatamente.
O proxy pode ser implementado como computador com estado, tendo os seguintes estados que emulam a funcionalidade de um disjuntor automático elétrico:
Fechado: o pedido da aplicação é encaminhado para a operação. O proxy mantém uma contagem do número de falhas recentes e, se a chamada para a operação não for bem-sucedida, o proxy aumenta esta contagem. Se o número de falhas recentes exceder um limiar especificado num determinado período de tempo, o proxy é colocado em estado Aberto. Neste ponto, o proxy inicia um temporizador de tempo limite e, quando esse temporizador expira, o proxy é colocado no estado Semiaberto.
O objetivo do temporizador de tempo limite é dar ao sistema tempo para corrigir o problema que causou a falha antes de permitir que o aplicativo tente executar a operação novamente.
Aberto: o pedido da aplicação falha imediatamente e uma exceção é devolvida à aplicação.
Meio-aberto: um número limitado de pedidos da aplicação pode passar e invocar a operação. Se estes pedidos forem bem-sucedidos, assume-se que a falha que estava a causar o problema foi corrigida e o disjuntor automático muda para o estado Fechado (a contagem de falhas é reposta). Se alguma solicitação falhar, o disjuntor assume que a falha ainda está presente para que ele reverta para o estado Open e reinicie o temporizador de tempo limite para dar ao sistema um período adicional de tempo para se recuperar da falha.
O estado Meio-Aberto é útil para impedir um serviço de recuperação de ficar repentinamente sobrecarregado com pedidos. À medida que o serviço recupera, este poderá conseguir suportar um volume limitado de pedidos até a recuperação ser concluída, mas enquanto a recuperação se encontra em curso, uma sobrecarga de trabalho pode fazer com que o serviço exceda o tempo limite ou falhe novamente.
Na figura, o contador de falhas utilizado pelo estado Fechado é baseado em tempo. É automaticamente reiniciado em intervalos periódicos. Este design ajuda a evitar que o disjuntor entre no estado Open se ocorrer falhas ocasionais. O limiar de falha que coloca o disjuntor automático em estado Aberto só é alcançado após um número especificado de falhas ter ocorrido durante um intervalo especificado. O contador utilizado pelo estado Meio-Aberto regista o número de tentativas bem-sucedidas de invocar a operação. O disjuntor automático regressa ao estado Fechado após um número especificado de invocações de operação consecutivas ter sito efetuado com êxito. Se alguma invocação falhar, o disjuntor automático entra imediatamente no estado Aberto e a contagem de tentativas bem-sucedidas é reposta da próxima vez que entrar no estado Meio-Aberto.
A forma como o sistema recupera é processada de forma externa, possivelmente ao restaurar ou reiniciar um componente falhado ou ao reparar uma ligaçã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 ao rejeitar rapidamente um pedido de uma operação com boas probabilidades de falhar, em vez de esperar que o limite de tempo de uma operação seja ultrapassado ou que esta nunca seja devolvida. Se o disjuntor automático gerar um evento sempre que muda de estado, esta informação pode ser utilizada para monitorizar o estado de funcionamento da parte do sistema protegida pelo disjuntor automático ou para alertar um administrador quando um disjuntor automático passar para o estado Aberto.
O padrão é personalizável e pode ser adaptado segundo o tipo da possível falha. Por exemplo, você pode aplicar um temporizador de tempo limite crescente a um disjuntor. Você pode colocar o disjuntor no estado Abrir por alguns segundos inicialmente e, se a falha não tiver sido resolvida, aumentar o tempo limite para alguns minutos e assim por diante. Em alguns casos, em vez de o estado Aberto devolver uma falha e gerar uma exceção, poderá ser útil regressar a um valor predefinido que seja significativo para a aplicação.
Nota
Tradicionalmente, os disjuntores dependiam de limites pré-configurados, como contagem de falhas e duração do tempo limite, resultando em um comportamento determinístico, mas às vezes subótimo. No entanto, técnicas adaptativas usando IA e ML podem ajustar dinamicamente os limites com base em padrões de tráfego em tempo real, anomalias e taxas de falha históricas, tornando o disjuntor mais resiliente e eficiente.
Problemas e considerações
Deve considerar os seguintes pontos ao decidir como implementar este padrão:
Tratamento de exceções: Um aplicativo que invoca uma operação através de um disjuntor deve estar preparado para lidar com as exceções levantadas se a operação não estiver disponível. A forma como as exceções são processadas depende da aplicação. Por exemplo, uma aplicação pode degradar temporariamente o funcionamento da mesma, invocar uma operação alternativa para tentar efetuar a mesma tarefa, obter os mesmos dados ou reportar a exceção ao utilizador e pedir a este que tente novamente mais tarde.
Tipos de exceções: Uma solicitação pode falhar por muitos motivos, alguns dos quais podem indicar um tipo mais grave de falha do que outros. Por exemplo, uma solicitação pode falhar porque um serviço remoto falhou e levará vários minutos para se recuperar, ou devido a um tempo limite devido ao serviço estar temporariamente sobrecarregado. Um disjuntor automático poderá conseguir examinar os tipos de exceções que ocorrem e ajustar a respetiva estratégia consoante a natureza dessas exceções. Por exemplo, pode ser necessário um número maior de exceções de tempo limite para levar o disjuntor ao estado Open em comparação com o número de falhas devido ao serviço estar completamente indisponível.
de monitoramento: Um disjuntor deve fornecer observabilidade clara em solicitações fracassadas e bem-sucedidas, permitindo que as equipes de operações avaliem a integridade do sistema. Use o rastreamento distribuído para visibilidade de ponta a ponta entre serviços.
de capacidade de recuperação: Você deve configurar o disjuntor para corresponder ao provável padrão de recuperação da operação que ele está protegendo. Por exemplo, se o disjuntor automático permanecer muito tempo no estado Aberto, poderá colocar exceções, mesmo que o motivo da falha tenha sido resolvido. De forma semelhante, um disjuntor automático pode aumentar e reduzir os tempos de respostas de aplicações se mudar demasiado rapidamente do estado Aberto para o estado Meio-Aberto.
Testando operações com falha: No estado Aberto, em vez de usar um temporizador para determinar quando alternar para o estado Semiaberto, um disjuntor pode, em vez disso, executar periodicamente ping no serviço remoto ou recurso para determinar se ele está disponível novamente. Este ping pode assumir a forma de uma tentativa de invocar uma operação que tinha anteriormente falhado, ou poderá utilizar uma operação especial fornecida pelo serviço remoto especificamente para testar o estado de funcionamento do serviço, conforme descrito pelo Padrão de Monitorização do Estado de Pontos Finais.
Substituição manual: Em um sistema onde o tempo de recuperação de uma operação com falha é extremamente variável, é benéfico fornecer uma opção de reinicialização manual que permite que um administrador feche um disjuntor (e redefina o contador de falhas). Da mesma forma, um administrador pode forçar um disjuntor no estado Open (e reiniciar o temporizador de tempo limite) se a operação protegida pelo disjuntor estiver temporariamente indisponível.
Simultaneidade: O mesmo disjuntor pode ser acessado por um grande número de instâncias simultâneas de um aplicativo. 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, num arquivo de dados que contenha diversas partições, uma partição poderá estar totalmente acessível, ficando a outra com um problema temporário. Se as respostas de erro nesses cenários forem unidas, uma aplicação poderá tentar aceder a algumas partições mesmo quando uma falha for provável, enquanto que o acesso a outras partições poderá estar bloqueado, não obstante as boas probabilidades de sucesso.
Interruptor acelerado: Às vezes, uma resposta de falha pode conter informações suficientes para que o disjuntor tropece imediatamente e permaneça tropeçado por um período mínimo de tempo. Por exemplo, a resposta de erro de um recurso partilhado que está sobrecarregado poderá indicar que uma nova tentativa imediata não é recomendada e que a aplicação deverá tentar novamente em poucos minutos.
Implantações em várias regiões: Um disjuntor pode ser projetado para implantações de uma ou várias regiões. Este último pode ser implementado usando balanceadores de carga globais ou estratégias personalizadas de circuit breaking com reconhecimento de região que garantem failover controlado, otimização de latência e conformidade normativa.
Disjuntores de malha de serviço: Os disjuntores podem ser implementados 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 de sidecar ou como um recurso autônomo sem modificar o código do aplicativo.
Nota
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 no momento. A resposta pode incluir informações adicionais, como a duração prevista do atraso.
Reproduzir solicitações com falha: No estado Open, 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 limite inadequados em serviços externos: um disjuntor pode não ser capaz de proteger totalmente os aplicativos contra operações que falham em serviços externos configurados com um longo período de tempo limite. 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 este período, muitas outras instâncias de aplicações poderão também tentar invocar o serviço através do disjuntor automático e ligar um número significativo de threads antes de todas falharem.
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, garantindo resiliência em arquiteturas heterogêneas.
Quando utilizar este padrão
Utilize este padrão:
- Para evitar falhas em cascata interrompendo invocações excessivas por um serviço remoto ou solicitações de acesso a um recurso compartilhado se essas operações tiverem alta probabilidade de falhar.
- Para aumentar a resiliência de várias regiões, roteando o tráfego de forma inteligente com base em sinais de falha em tempo real.
- Para proteger contra dependências lentas, ajudando-o a acompanhar seus SLOs (Service Level Objetives, objetivos de nível de serviço) e a evitar a degradação do desempenho devido a serviços de alta latência.
- Para lidar com problemas intermitentes de conectividade e reduzir falhas de solicitação em ambientes distribuídos.
Este padrão não é recomendado:
- Para gerir o acesso a recursos privados locais numa aplicação, como a estrutura de dados na memória. Neste ambiente, utilizar um disjuntor automático adicionaria uma sobrecarga ao seu sistema.
- Como substituto para processar exceções na lógica de negócio das suas aplicações.
- Quando algoritmos de repetição bem conhecidos são suficientes e suas dependências são projetadas para lidar com mecanismos de repetição. A implementação de um disjuntor na sua aplicação, neste caso, pode adicionar complexidade desnecessária ao seu sistema.
- Ao esperar por um disjuntor para redefinir pode introduzir atrasos inaceitáveis.
- Se você tiver uma arquitetura orientada por mensagem ou por eventos, pois eles geralmente encaminham mensagens com falha para uma fila de mensagens mortas (DLQ) para processamento manual ou adiado. Os mecanismos internos de isolamento e repetição de falhas normalmente implementados nesses projetos geralmente são suficientes.
- Se a recuperação de falhas for 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, os disjuntores podem não ser necessários.
Design da carga de trabalho
Um arquiteto deve avaliar como o padrão Disjuntor pode ser usado no design de sua carga de trabalho para abordar as metas e princípios abordados nos pilares do Azure Well-Architected Framework. Por exemplo:
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 a garantir que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. | Esse padrão evita sobrecarregar uma dependência com falha. Você também pode usar esse padrão para acionar a degradação normal na carga de trabalho. Os disjuntores são frequentemente acoplados à recuperação automática para fornecer autopreservação e auto-cura. - RE:03 Análise do modo de falha - RE:07 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 repetição em erro, que pode levar à utilização excessiva de recursos durante a recuperação de dependência e também pode sobrecarregar o desempenho em uma dependência que está tentando recuperar. - PE:07 Código e infraestrutura - PE:11 Respostas a questões em direto |
Como em qualquer decisão de design, considere quaisquer compensações em relação aos objetivos dos outros pilares que possam ser introduzidos com esse padrão.
Recursos relacionados
Os padrões seguintes podem também ser úteis ao implementar este padrão:
O padrão de aplicativo Web confiável mostra como aplicar o padrão de disjuntor a aplicativos Web convergentes na nuvem.
Padrão Repetição. Descreve como uma aplicação pode processar falhas previstas e temporárias quando tentar ligar a um recurso ou serviço de rede, ao repetir de forma transparente uma operação que falhou anteriormente.
Padrão de monitoramento de ponto final de integridade. Um disjuntor automático pode conseguir testar o estado de um serviço ao enviar um pedido ao ponto final exposto pelo serviço. O serviço deve devolver informações a indicar o seu estado.