Design de autorrecuperação

Desenvolva seu aplicativo para realizar autorrecuperação quando houver falhas

Em um sistema distribuído, podem ocorrer falhas. O hardware pode falhar. A rede pode ter falhas transitórias. Raramente, um serviço, data center ou região do Azure inteira sofrerá uma interrupção. Mesmo assim, devemos estar preparados para isso.

Portanto, desenvolva um aplicativo para realizar autorrecuperação quando houver falhas. Isso requer uma abordagem em três fases:

  • Detectar falhas.
  • Responder a falhas normalmente.
  • Registre em log e monitore falhas para fornecer insights operacionais.

Como você responde a um determinado tipo de falha pode depender dos requisitos de disponibilidade do seu aplicativo. Por exemplo, se você precisar de alta disponibilidade, poderá implantar em várias zonas de disponibilidade em uma região. Para evitar interrupções, mesmo no caso improvável de uma região do Azure inteira sofrer interrupções, você pode fazer failover automaticamente para uma região secundária durante uma interrupção regional. No entanto, isso acarretará um custo maior e um desempenho potencialmente menor do que uma implantação de região única.

Além disso, não considere apenas grandes eventos, como interrupções regionais, que costumam ser raros. Você deve focar tanto ou mais no tratamento de falhas locais de curta duração, como falhas de conectividade de rede ou falhas em conexões com o banco de dados.

Recomendações

Repita as operações com falha. As falhas transitórias podem ocorrer por perda momentânea de conectividade de rede, interrupção na conexão de banco de dados ou tempo limite atingido quando um serviço está ocupado. Crie lógica de novas tentativas em seu aplicativo para lidar com falhas transitórias. Para muitos serviços do Azure, o SDK do cliente implementa novas tentativas automáticas. Para saber mais, confira Transient fault handling (Tratamento de falha transitória) e Retry pattern (Padrão de nova tentativa).

Proteger os serviços remotos com falha (interruptor de circuito). É bom tentar novamente após uma falha temporária, mas se a falha persistir, você poderá acabar com muitos chamadores insistindo em um serviço com falha. Isso pode levar a falhas em cascata, conforme as solicitações se acumulam. Use o padrão do Interruptor de Circuito para falhar rapidamente (sem fazer a chamada remota) quando uma operação provavelmente for falhar.

Isolar recursos críticos (bulkhead). Falhas em um subsistema às vezes podem formar uma cascata. Isso poderá acontecer se uma falha impedir que alguns recursos, como threads ou soquetes, sejam liberados em tempo hábil, exaurindo recursos. Para evitar isso, use o Padrão de bulkhead para particionar um sistema em grupos isolados para que a falha em uma partição não deixe todo o sistema inoperante.

Executar o nivelamento de carga. Os aplicativos podem enfrentar picos repentinos no tráfego, que podem sobrecarregar o serviços no back-end. Para evitar isso, use o Padrão de Nivelamento de Carga Baseado em Fila para enfileirar itens de trabalho para que sejam executados de maneira assíncrona. A fila atua como buffer e diminui os picos na carga.

Failover. Se não for possível alcançar uma instância, faça o failover para outra instância. Para elementos sem estado, como um servidor Web, coloque várias instâncias atrás de um balanceador de carga ou gerenciador de tráfego. Para elementos que armazenam estado, como um banco de dados, use réplicas e failover. Dependendo do armazenamento de dados e como ele replica, isso pode exigir que o aplicativo lide com possível consistência.

Compensar transações com falha. Em geral, evite transações distribuídas, pois elas exigem a coordenação entre serviços e recursos. Em vez disso, componha uma operação usando transações individuais menores. Se a operação falhar no meio, use Como compensar transações para desfazer qualquer etapa já concluída.

Executar ponto de verificação de transações de execução longa. Pontos de verificação podem oferecer resiliência quando uma operação de execução longa falha. Quando a operação é reiniciada (por exemplo, é capturada por outra VM), ela pode ser retomada do último ponto de verificação. Considere implementar um mecanismo que registre informações de estado da tarefa em intervalos regulares e salve esse estado em armazenamento durável que possa ser acessado por qualquer instância do processo executando a tarefa. Dessa forma, se o processo for desligado, o trabalho que ele estava executando poderá ser retomado do último ponto de verificação por meio de outra instância. Há bibliotecas que fornecem essa funcionalidade, como NServiceBus e MassTransit. Elas persistem de forma transparente e os intervalos são alinhados com o processamento de mensagens de filas no Barramento de Serviço do Azure.

Degradar normalmente. Às vezes, você não pode solucionar um problema, mas pode fornecer funcionalidade reduzida que ainda seja útil. Considere um aplicativo que mostra um catálogo de livros. Se o aplicativo não puder recuperar a imagem em miniatura da capa, poderá mostrar uma imagem de espaço reservado. Subsistemas inteiros podem não ser críticos para o aplicativo. Por exemplo, em um site de comércio eletrônico, mostrar as recomendações de produto é provavelmente menos crítico que processar pedidos.

Limitar os clientes. Às vezes, um pequeno número de usuários cria carga excessiva, o que pode reduzir a disponibilidade do aplicativo para outros usuários. Nessa situação, limite o cliente a um determinado período. Confira o Padrão de limitação.

Bloquear atores ruins. Só porque você limita um cliente, isso não significa cliente estava agindo maliciosamente. Significa apenas que o cliente excedeu sua cota de serviço. Porém, se um cliente consistentemente exceder sua cota ou comporta-se incorretamente de outra maneira, você poderá bloqueá-lo. Defina um processo fora de banda para o usuário solicitar seu desbloqueio.

Usar eleição de líder. Quando você precisar coordenar uma tarefa, use Eleição de Líder para selecionar um coordenador. Dessa forma, o coordenador não será um ponto único de falha. Se o coordenador falhar, um novo será selecionado. Em vez de implementar um algoritmo de eleição líder do zero, considere uma solução comercial, como Zookeeper.

Teste com injeção de falha. Com muita frequência, o caminho de sucesso é bem testado, mas não o caminho de falha. Um sistema pode executar em produção por um longo tempo antes que um caminho de falha seja utilizado. Use a injeção de teste para testar a resiliência do sistema a falhas, seja disparando falhas reais ou simulando-as.

Adotar a engenharia caos. A engenharia do caos estende a noção de injeção de falha injetando aleatoriamente falhas ou condições anormais em instâncias de produção.

Considere usar zonas de disponibilidade. Muitas regiões do Azure fornecem zonas de disponibilidade, que são conjuntos isolados de data centers dentro da região. Alguns serviços do Azure podem ser implantados em zonas, o que garante que eles sejam colocados em uma zona específica e possam ajudar a reduzir a latência na comunicação entre componentes na mesma carga de trabalho. Como alternativa, alguns serviços podem ser implantados com redundância de zona, o que significa que o Azure replica automaticamente o recurso entre zonas para alta disponibilidade. Considere qual abordagem fornece o melhor conjunto de compensações para sua solução. Para saber mais sobre como projetar sua solução para usar zonas e regiões de disponibilidade, consulte Recomendações para usar zonas de disponibilidade e regiões.

Para uma abordagem estruturada para tornar seus aplicativos aptos a realizar autorrecuperação, confira Design de aplicativos resilientes para o Azure.