Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Sugestão
Este conteúdo é um excerto do eBook Architecting Cloud Native .NET Applications for Azure, disponível no .NET Docs ou como um PDF transferível gratuito que pode ser lido offline.
A primeira linha de defesa é a resiliência da aplicação.
Embora você possa investir tempo considerável escrevendo sua própria estrutura de resiliência, esses produtos já existem.
Polly é uma biblioteca abrangente de resiliência .NET e tratamento de falhas transitórias que permite que os desenvolvedores expressem políticas de resiliência de maneira fluente e segura para threads. O Polly destina-se a aplicativos criados com o .NET Framework ou o .NET 7. A tabela a seguir descreve os recursos de resiliência, chamados policies, disponíveis na Biblioteca Polly. Podem ser aplicados individualmente ou agrupados.
| Política | Experiência |
|---|---|
| Tentar novamente | Configura operações de nova tentativa em operações designadas. |
| Disjuntor Automático | Bloqueia operações solicitadas por um período predefinido quando as falhas excedem um limite configurado |
| Interrupção temporária | Coloca limite na duração pela qual um chamador pode esperar por uma resposta. |
| Antepara | Restringe as ações a um pool de recursos de tamanho fixo para evitar que chamadas falhadas sobrecarreguem um recurso. |
| cache | Armazena respostas automaticamente. |
| Contingência | Define o comportamento estruturado em caso de falha. |
Observe como na figura anterior as políticas de resiliência se aplicam a mensagens de solicitação, sejam elas provenientes de um cliente externo ou de um serviço back-end. O objetivo é compensar a solicitação de um serviço que pode estar momentaneamente indisponível. Essas interrupções de curta duração normalmente se manifestam com os códigos de status HTTP mostrados na tabela a seguir.
| Código de estado HTTP | Motivo |
|---|---|
| 404 | Não encontrado |
| 408 | Tempo limite da requisição |
| 429 | Demasiados pedidos (provavelmente foi limitado) |
| 502 | Erro de gateway |
| 503 | Serviço indisponível |
| 504 | Tempo limite do gateway |
Pergunta: Você tentaria novamente um código de status HTTP de 403 - Proibido? Não. Aqui, o sistema está funcionando corretamente, mas informando ao chamador que ele não está autorizado a executar a operação solicitada. Deve-se ter o cuidado de repetir apenas as operações causadas por falhas.
Conforme recomendado no Capítulo 1, os desenvolvedores da Microsoft que criam aplicativos nativos da nuvem devem ter como alvo a plataforma .NET. A versão 2.1 introduziu a biblioteca HTTPClientFactory para criar instâncias de cliente HTTP para interagir com recursos baseados em URL. Substituindo a classe HTTPClient original, a classe factory suporta muitos recursos aprimorados, um dos quais é a integração total com a biblioteca de resiliência Polly. Com ele, você pode facilmente definir políticas de resiliência na classe de inicialização do aplicativo para lidar com falhas parciais e problemas de conectividade.
Em seguida, vamos expandir os padrões de repetição de tentativa e de disjuntor.
Padrão de Repetição
Em um ambiente distribuído nativo da nuvem, as chamadas para serviços e recursos de nuvem podem falhar devido a falhas transitórias (de curta duração), que normalmente se corrigem após um breve período de tempo. A implementação de uma estratégia de repetição ajuda um serviço nativo da nuvem a mitigar esses cenários.
O padrão Retry permite que um serviço tente novamente uma operação de solicitação com falha um número (configurável) de vezes com um tempo de espera exponencialmente crescente. A Figura 6-2 mostra uma nova tentativa em ação.
Figura 6-2. Padrão de repetição de tentativa em ação
Na figura anterior, um padrão de nova tentativa foi implementado para uma operação de pedido. Está configurado para permitir até quatro tentativas antes de falhar, com um intervalo de espera iniciando em dois segundos, e o intervalo dobra exponencialmente a cada tentativa subsequente.
- A primeira chamada falha e retorna um código de status HTTP de 500. O aplicativo aguarda por dois segundos e tenta novamente a chamada.
- A segunda invocação também falha e retorna um código de status HTTP de 500. O aplicativo agora dobra o intervalo de backoff para quatro segundos e tenta novamente a chamada.
- Finalmente, a terceira chamada é bem-sucedida.
- Nesse cenário, a operação de repetição teria tentado até quatro novas tentativas, dobrando a duração do backoff antes de falhar a chamada.
- Se a 4ª tentativa falhasse, uma política de fallback seria invocada para lidar graciosamente com o problema.
É importante aumentar o período de espera antes de tentar a chamada novamente para permitir que o serviço tenha tempo para se corrigir automaticamente. É uma boa prática implementar um backoff exponencialmente crescente (dobrando o período em cada nova tentativa) para permitir um tempo de correção adequado.
Padrão do disjuntor
Embora o padrão de repetição possa ajudar a salvar uma solicitação emaranhada em uma falha parcial, há situações em que as falhas podem ser causadas por eventos imprevistos que exigirão longos períodos de tempo para serem resolvidos. Estas falhas podem variar em termos de gravidade, de uma perda parcial de conectividade à falha total de um serviço. Nessas situações, é inútil para um aplicativo repetir continuamente uma operação que provavelmente não terá êxito.
Para piorar as coisas, a execução de operações de repetição contínua em um serviço não responsivo pode levá-lo a um cenário de negação de serviço autoimposto, onde você inunda seu serviço com chamadas contínuas esgotando recursos como memória, threads e conexões de banco de dados, causando falha em partes não relacionadas do sistema que usam os mesmos recursos.
Nessas situações, seria preferível que a operação falhasse imediatamente e só tentasse invocar o serviço se for provável que ele seja bem-sucedido.
O padrão de disjuntor pode impedir que um aplicativo tente executar repetidamente uma operação que provavelmente falhará. Após um número predefinido de chamadas falhadas, bloqueia todo o tráfego para o serviço. Periodicamente, permitirá uma chamada de teste para determinar se a falha foi resolvida. A Figura 6-3 mostra o padrão do disjuntor em ação.
Figura 6-3. Padrão do disjuntor em ação
Na figura anterior, um padrão de 'Circuit Breaker' foi adicionado ao padrão de repetição original. Observe como após 100 solicitações com falha, os disjuntores abrem e não permitem mais chamadas para o serviço. O valor CheckCircuit, definido em 30 segundos, especifica a frequência com que a biblioteca permite que uma solicitação prossiga para o serviço. Se essa chamada for bem-sucedida, o circuito fecha e o serviço fica novamente disponível para o tráfego.
Tenha em mente que a intenção do padrão Disjuntor é diferente da do padrão Retry. O padrão Retry permite que um aplicativo tente novamente uma operação na expectativa de que ela seja bem-sucedida. O padrão de disjuntor impede que um aplicativo faça uma operação que provavelmente falhará. Normalmente, um aplicativo combinará esses dois padrões usando o padrão Retry para invocar uma operação por meio de um disjuntor.
Testes de resiliência
O teste de resiliência nem sempre pode ser feito da mesma forma que você testa a funcionalidade do aplicativo (executando testes de unidade, testes de integração e assim por diante). Em vez disso, você deve testar o desempenho da carga de trabalho de ponta a ponta em condições de falha, que ocorrem apenas intermitentemente. Por exemplo: injetar falhas ao travar processos, certificados expirados, tornar serviços dependentes indisponíveis etc. Estruturas como o macaco do caos podem ser usadas para esses testes de caos.
A resiliência do aplicativo é essencial para lidar com operações solicitadas problemáticas. Mas, é apenas metade da história. Em seguida, abordamos os recursos de resiliência disponíveis na nuvem do Azure.