Resiliência de aplicativos e infraestrutura

Concluído

A resiliência é a capacidade de recuperar de falhas transitórias. A estratégia de recuperação da aplicação recupera o funcionamento normal, com impacto mínimo para o utilizador. Falhas podem acontecer em ambientes de nuvem, e seu aplicativo deve responder de forma a minimizar o tempo de inatividade e a perda de dados. Em uma situação ideal, seu aplicativo lida com falhas graciosamente sem que o usuário saiba que houve um problema.

Como os ambientes de microsserviço podem ser voláteis, projete seus aplicativos para esperar e lidar com falhas parciais. Uma falha parcial, por exemplo, pode incluir exceções de código, interrupções de rede, processos de servidor sem resposta ou falhas de hardware. Mesmo atividades planejadas, como mover contêineres para um nó diferente dentro de um cluster do Kubernetes, podem causar uma falha transitória.

Abordagens de resiliência

Ao projetar aplicativos resilientes, muitas vezes você tem que escolher entre falha rápida e degradação graciosa. Falhar rapidamente significa que o aplicativo lançará imediatamente um erro ou exceção quando algo der errado, em vez de tentar recuperar ou contornar o problema. Isso permite que os problemas sejam identificados e corrigidos rapidamente. A degradação normal significa que o aplicativo tentará continuar operando em uma capacidade limitada, mesmo quando algum componente falhar.

Em aplicativos nativos da nuvem, é importante que os serviços lidem com falhas normalmente, em vez de falhar rapidamente. Como os microsserviços são descentralizados e implantáveis de forma independente, são esperadas falhas parciais. Uma falha rápida permitiria que uma falha em um serviço derrubasse rapidamente os serviços dependentes, o que reduz a resiliência geral do sistema. Em vez disso, os microsserviços devem ser codificados para antecipar e tolerar falhas de serviço internas e externas. Essa degradação graciosa permite que o sistema geral continue operando mesmo se alguns serviços forem interrompidos. Funções críticas voltadas para o usuário podem ser mantidas, evitando uma interrupção completa. Uma falha normal também permite que os serviços perturbados tenham tempo para se recuperar ou se auto-curar antes de afetar o resto do sistema. Portanto, para aplicativos baseados em microsserviços, a degradação normal se alinha melhor às práticas recomendadas de resiliência, como isolamento de falhas e recuperação rápida. Evita que incidentes locais se espalhem em cascata pelo sistema.

Existem duas abordagens fundamentais para suportar uma degradação graciosa com resiliência: aplicação e infraestrutura. Cada abordagem tem as suas vantagens e desvantagens. Ambas podem ser adequadas, dependendo da situação. Este módulo explica como implementar resiliência baseada em código e em infraestrutura.

Resiliência baseada no código

Para implementar resiliência baseada em código, o .NET tem uma biblioteca de extensão para resiliência e tratamento de falhas transitórias, Microsoft.Extensions.Http.Resilience.

Ele usa uma sintaxe fluente e fácil de entender para criar código de manipulação de falhas de maneira segura para threads. Existem várias políticas de resiliência que definem o comportamento de processamento de falhas. Neste módulo, você aplica as estratégias de repetição e disjuntor às operações do cliente HTTP.

Estratégia de repetição

Uma estratégia Retry é exatamente o que o nome indica. O pedido é repetido após um breve período de espera, se for recebida uma resposta de erro. O tempo de espera aumenta a cada nova tentativa. O aumento pode ser linear ou exponencial.

Depois que o número máximo de tentativas é atingido, a estratégia desiste e lança uma exceção. Do ponto de vista do usuário, o aplicativo geralmente leva mais tempo para concluir algumas operações. O aplicativo também pode levar algum tempo antes de informar ao usuário que não foi possível concluir a operação.

Estratégia de disjuntor

Uma estratégia de disjuntor dá a um serviço alvo uma pausa após um número repetido de falhas, pausando a tentativa de se comunicar com ele. O serviço pode estar a ter um problema grave e ficar temporariamente impossibilitado de responder. Após um número definido de falhas consecutivas, as tentativas de conexão são pausadas, abrindo o circuito. Durante essa espera, operações adicionais no serviço de destino falham imediatamente sem sequer tentar conectar o serviço. Uma vez terminado o tempo de espera, será feita uma nova tentativa de efetuar a operação. Se o serviço responder com sucesso, o circuito é fechado e o sistema volta ao normal.

Resiliência baseada na infraestrutura

Para implementar resiliência baseada na infraestrutura, pode utilizar uma malha de serviços. Além de proporcionar resiliência sem alterar o código, uma malha de serviços fornece gestão de tráfego, políticas, segurança, identidade forte e observabilidade. A sua aplicação é desassociada destas capacidades operacionais, que serão movidas para a camada da infraestrutura.

Comparação com as abordagens baseadas no código

Uma abordagem de resiliência baseada em infraestrutura pode usar uma visão baseada em métricas que permite que ela se adapte dinamicamente às condições do cluster em tempo real. Esta abordagem adiciona uma nova dimensão à gestão do cluster, mas não adiciona código.

Com uma abordagem baseada em código, você:

  • Tem de adivinhar quais são os parâmetros adequados de repetição e tempo limite.
  • Foca-se num pedido HTTP específico.

Não existe uma forma razoável de responder a uma falha de infraestrutura no código da sua aplicação. Considere as centenas ou milhares de pedidos que estão a ser processados em simultâneo. Uma simples repetição com término exponencial (vezes o número de pedidos) pode inundar um serviço.

Por outro lado, as abordagens baseadas em infraestrutura não estão cientes dos internos do aplicativo. Por exemplo, transações complexas de banco de dados são invisíveis para malhas de serviço. Tais transações só podem ser protegidas contra falhas com uma abordagem baseada em código.

Nas próximas unidades, você implementará resiliência para um aplicativo baseado em microsserviço usando resiliência HTTP .NET no código e uma malha de serviço Linkerd.