Resiliência de aplicativos e infraestrutura
Resiliência é a capacidade de se recuperar de falhas transitórias. A estratégia de recuperação do aplicativo restaura a função normal com impacto mínimo para o usuário. Falhas podem ocorrer em ambientes de nuvem, e seu aplicativo deve responder de uma forma que minimize o tempo de inatividade e a perda de dados. Em uma situação ideal, seu aplicativo lida com falhas normalmente, 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. Até mesmo atividades planejadas, como mover contêineres para outro nó dentro de um cluster do Kubernetes, podem causar uma falha transitória.
Abordagens à resiliência
Ao projetar aplicativos resilientes, muitas vezes você precisa 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.
Nos aplicativos nativos de nuvem, é importante que os serviços tratem as falhas suavemente, ao invés de falharem rapidamente. Como os microsserviços são descentralizados e implantáveis independentemente, falhas parciais são esperadas. Falhar rapidamente 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 leve 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. A falha leve também permite que os serviços afetados tenham tempo para se recuperar ou resolver antes de afetar o resto do sistema. Portanto, para aplicativos baseados em microsserviços, a degradação leve se alinha melhor às práticas recomendadas de resiliência, como isolamento de falhas e recuperação rápida. Ela evita que incidentes locais ocorram em cascata em todo o sistema.
Há duas abordagens fundamentais para aceitar uma degradação leve com resiliência: aplicativo e infraestrutura. Cada uma delas tem benefícios e desvantagens. As duas podem ser adequadas dependendo da situação. Este módulo explica como implementar a resiliência baseada em código e baseada em infraestrutura.
Resiliência baseada em código
Para implementar a resiliência baseada em código, .NET tem uma biblioteca de extensão voltada à resiliência e ao tratamento de falhas transitóriasMicrosoft.Extensions.Http.Resilience
.
Ele usa uma sintaxe fluente e fácil de entender para criar código de tratamento de falhas de maneira thread-safe. Há várias políticas de resiliência que definem o comportamento de tratamento de falhas. Neste módulo, você aplica as estratégias de Repetição e de Disjuntor às operações de cliente HTTP.
Estratégia de repetição
Uma estratégia de Repetição é exatamente o que o nome indica. A solicitação é repetida após uma breve espera quando uma resposta de erro é recebida. 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. Da perspectiva do usuário, o aplicativo geralmente leva mais tempo para concluir algumas operações. O aplicativo também pode levar algum tempo para informar ao usuário que não foi possível concluir a operação.
Estratégia do disjuntor
Uma Estratégia de Disjuntor dá a um serviço alvo uma pausa após um número repetido de falhas, pausando tentando se comunicar com ele. O serviço pode estar enfrentando um problema sério e ficar temporariamente incapaz de responder. Após um número definido de falhas consecutivas, as tentativas de conexão são pausadas, abrindo o circuito. Nesse período de espera, operações adicionais no serviço de destino falham imediatamente, sem nem mesmo tentar conectar o serviço. Depois que o tempo de espera tiver decorrido, a operação será tentada novamente. Se o serviço responder com êxito, o circuito será fechado e o sistema voltará ao normal.
Resiliência baseada em infraestrutura
Para implementar a resiliência baseada em infraestrutura, você pode usar uma malha de serviço. Além de proporcionar resiliência sem alterar o código, uma malha de serviço fornece gerenciamento de tráfego, política, segurança, identidade forte e observabilidade. O aplicativo é separado desses recursos operacionais, que são movidos para a camada de infraestrutura.
Comparação com as abordagens baseadas em código
Uma abordagem de resiliência baseada em infraestrutura pode usar uma exibição baseada em métricas que permite que ela se adapte dinamicamente às condições do cluster em tempo real. Essa abordagem adiciona outra dimensão ao gerenciamento do cluster, mas não adiciona nenhum código.
Com uma abordagem baseada em código, você:
- Precisa adivinhar quais parâmetros de repetição e de tempo limite são apropriados.
- Concentra-se em uma solicitação HTTP específica.
Não há uma forma razoável de responder a uma falha de infraestrutura no código do aplicativo. Considere as centenas ou milhares de solicitações que estão sendo processadas simultaneamente. Até mesmo uma repetição com retirada (contagem de solicitações de tempo) exponencial pode inundar um serviço.
Por outro lado, abordagens baseadas em infraestrutura desconhecem os elementos internos do aplicativo. Por exemplo, transações de banco de dados complexas são invisíveis para as malhas de serviço. Essas 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 do .NET no código e uma malha de serviço Linkerd.