Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Dica
Esse conteúdo é um trecho do eBook, Architecting Cloud Native .NET Applications for Azure, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
A primeira linha de defesa é a resiliência do aplicativo.
Embora você possa investir um tempo considerável escrevendo sua própria estrutura de resiliência, esses produtos já existem.
A Polly é uma biblioteca abrangente de resiliência do .NET e tratamento de falhas transitórias que permite que os desenvolvedores expressem políticas de resiliência de maneira fluente e thread-safe. O Polly tem como destino aplicativos criados com o .NET Framework ou o .NET 7. A tabela a seguir descreve as funcionalidades de resiliência, chamadas policies, disponíveis na Biblioteca Polly. Eles podem ser aplicados individualmente ou agrupados.
| Política | Experiência |
|---|---|
| Tentar novamente | Configura operações de repetição em operações designadas. |
| Disjuntor | Bloqueia as operações solicitadas para um período predefinido quando as falhas excedem um limite configurado |
| Intervalo | Coloca o limite de duração para o qual um chamador pode aguardar uma resposta. |
| Antepara | Restringe as ações ao pool de recursos de tamanho fixo para impedir que chamadas com falha prejudiquem um recurso. |
| armazenamento em cache | Armazena respostas automaticamente. |
| Alternativa | Define o comportamento estruturado após uma falha. |
Observe como, na figura anterior, as políticas de resiliência se aplicam a mensagens de solicitação, seja provenientes de um cliente externo ou de um serviço de back-end. A meta é compensar a solicitação de um serviço que possa 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 Status HTTP | Motivo |
|---|---|
| 404 | Não encontrado |
| 408 | Tempo limite da solicitação |
| 429 | Muitas solicitações (provavelmente há uma restrição) |
| 502 | Gateway inválido |
| 503 | Serviço indisponível |
| 504 | Tempo limite do gateway |
Pergunta: Você tentaria novamente um código de status HTTP 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 tomar cuidado para tentar novamente apenas as operações causadas por falhas.
Conforme recomendado no Capítulo 1, os desenvolvedores da Microsoft que construirem aplicativos nativos de nuvem devem ter como destino 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 de fábrica dá suporte a muitos recursos aprimorados, um dos quais é uma integração apertada com a biblioteca de resiliência polly. Com ele, você pode definir facilmente políticas de resiliência na classe de inicialização do aplicativo para lidar com falhas parciais e problemas de conectividade.
Depois, vamos detalhar melhor os padrões de repetição e disjuntor.
Padrão de repetição
Em um ambiente nativo de nuvem distribuída, 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. Implementar uma estratégia de repetição ajuda um serviço nativo de nuvem a reduzir esses cenários.
O padrão de repetição permite que um serviço repita 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 repetição em ação.
Figura 6-2. Padrão de repetição em ação
Na figura anterior, um padrão de nova tentativa foi implementado para uma operação de solicitação. Ele é configurado para permitir até quatro novas tentativas antes de falhar com um intervalo de retirada (tempo de espera) começando em dois segundos, o que dobra exponencialmente para cada tentativa subsequente.
- A primeira invocação falha e retorna um código de status HTTP de 500. O aplicativo aguarda 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 retirada para quatro segundos e tenta novamente a chamada.
- Por fim, a terceira chamada é bem-sucedida.
- Nesse cenário, a operação de repetição teria feito até quatro tentativas duplicando a duração da retirada antes de falhar a chamada.
- Se a 4ª tentativa de repetição tivesse falhado, uma política de fallback seria invocada para lidar normalmente com o problema.
É importante aumentar o período de retirada antes de repetir a chamada para permitir que o tempo de serviço seja corrigido por conta própria. É uma boa prática implementar um recuo exponencialmente crescente (dobrando o período em cada tentativa) para permitir um tempo de correção adequado.
Padrão de 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 falhas podem ser causadas por eventos imprevistos que exigirão períodos mais longos de tempo para serem resolvidos. Essas falhas podem variar de gravidade de uma perda parcial de conectividade até a falha completa de um serviço. Nessas situações, é inútil que um aplicativo repita continuamente uma operação que é improvável de ter êxito.
Para piorar as coisas, executar operações de repetição contínuas em um serviço não responsivo pode movê-lo para um cenário de negação de serviço autoimposto em que 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 apenas tentasse invocar o serviço se houver chance de sucesso.
O Circuit Breaker pattern pode impedir que um aplicativo tente executar repetidamente uma operação com alta probabilidade de falha. Depois de um número predefinido de chamadas com falha, ele bloqueia todo o tráfego para o serviço. Periodicamente, ele permitirá uma chamada de avaliação para determinar se a falha foi resolvida. A Figura 6-3 mostra o padrão Circuit Breaker em ação.
Figura 6-3. Padrão de disjuntor em ação
Na figura anterior, um padrão de disjuntor foi adicionado ao padrão de repetição original. Observe como, após 100 solicitações com falha, os disjuntores são abertos e não permitem mais chamadas para o serviço. O valor CheckCircuit, definido em 30 segundos, especifica com que frequência a biblioteca permite que uma solicitação prossiga para o serviço. Se essa chamada for bem-sucedida, o circuito será fechado e o serviço estará novamente disponível para tráfego.
Tenha em mente que a intenção do padrão Circuit Breaker é diferente da do padrão Retry. O padrão de repetição permite que um aplicativo repita uma operação na expectativa de que ele terá êxito. O padrão 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 de Tentativa para invocar uma operação por meio de um circuit breaker.
Teste de resiliência
O teste de resiliência nem sempre pode ser feito da mesma maneira 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 como a carga de trabalho de ponta a ponta é executada em condições de falha, que só ocorrem intermitentemente. Por exemplo: injetar falhas por parada de processos, certificados expirados, tornar os serviços dependentes indisponíveis etc. Frameworks como chaos-monkey podem ser usados para testes de caos.
A resiliência do aplicativo é necessária para lidar com operações solicitadas problemáticas. Mas é apenas metade da história. Em seguida, abordaremos os recursos de resiliência disponíveis na nuvem do Azure.