Compartilhar via


Cache distribuído (criando aplicativos de nuvem Real-World com o Azure)

por Rick Anderson, Tom Dykstra

Baixar Corrigir Projeto ou Baixar Livro Eletrônico

O livro eletrônico Building Real World Cloud Apps with Azure é baseado em uma apresentação desenvolvida por Scott Guthrie. Ele explica 13 padrões e práticas que podem ajudá-lo a desenvolver aplicativos Web para a nuvem com êxito. Para obter informações sobre o livro eletrônico, consulte o primeiro capítulo.

O capítulo anterior analisou o tratamento de falhas transitórias e mencionou o cache como uma estratégia de disjuntor. Este capítulo fornece mais informações sobre o cache, incluindo quando usá-lo, padrões comuns para usá-lo e como implementá-lo no Azure.

O que é o cache distribuído

Um cache fornece alta taxa de transferência, acesso de baixa latência a dados de aplicativo acessados com frequência, armazenando os dados na memória. Para um aplicativo de nuvem, o tipo mais útil de cache é o cache distribuído, o que significa que os dados não são armazenados na memória do servidor Web individual, mas em outros recursos de nuvem, e os dados armazenados em cache são disponibilizados para todos os servidores Web de um aplicativo (ou outras VMs de nuvem usadas pelo aplicativo).

diagrama mostrando vários servidores Web acessando os mesmos servidores de cache

Quando o aplicativo é dimensionado adicionando ou removendo servidores ou quando os servidores são substituídos devido a atualizações ou falhas, os dados armazenados em cache permanecem acessíveis a todos os servidores que executam o aplicativo.

Ao evitar o acesso a dados de alta latência de um armazenamento de dados persistente, o cache pode melhorar drasticamente a capacidade de resposta do aplicativo. Por exemplo, recuperar dados do cache é muito mais rápido do que recuperá-los de um banco de dados relacional.

Um benefício colateral do cache é a redução do tráfego para o armazenamento de dados persistente, o que pode resultar em custos menores quando há encargos de saída de dados para o armazenamento de dados persistente.

Quando usar o cache distribuído

O cache funciona melhor para cargas de trabalho de aplicativo que fazem mais leitura do que gravação de dados e quando o modelo de dados dá suporte à organização de chave/valor que você usa para armazenar e recuperar dados em cache. Também é mais útil quando os usuários do aplicativo compartilham muitos dados comuns; Por exemplo, o cache não ofereceria tantos benefícios se cada usuário normalmente recuperasse dados exclusivos para esse usuário. Um exemplo em que o cache pode ser muito benéfico é um catálogo de produtos, porque os dados não mudam com frequência e todos os clientes estão examinando os mesmos dados.

O benefício do cache torna-se cada vez mais mensurável quanto mais um aplicativo é dimensionado, à medida que os limites de taxa de transferência e atrasos de latência do armazenamento de dados persistentes se tornam mais um limite para o desempenho geral do aplicativo. No entanto, você também pode implementar o cache por outros motivos além do desempenho. Para dados que não precisam estar perfeitamente atualizados quando mostrados ao usuário, o acesso ao cache pode servir como um disjuntor para quando o armazenamento de dados persistente não estiver respondendo ou indisponível.

Para poder recuperar dados do cache, você precisa armazená-los lá primeiro. Há várias estratégias para obter dados necessários em um cache:

  • Sob demanda/Cache à parte

    O aplicativo tenta recuperar dados do cache e, quando o cache não tem os dados (um "erro"), o aplicativo armazena os dados no cache para que eles fiquem disponíveis na próxima vez. Na próxima vez que o aplicativo tentar obter os mesmos dados, ele encontrará o que está procurando no cache (um "clique"). Para evitar a busca de dados armazenados em cache que foram alterados no banco de dados, invalide o cache ao fazer alterações no armazenamento de dados.

  • Envio por push de dados em segundo plano

    Os serviços em segundo plano efetuam push de dados para o cache em um agendamento regular e o aplicativo sempre efetua pull do cache. Essa abordagem funciona muito bem com fontes de dados de alta latência que não exigem que você sempre retorne os dados mais recentes.

  • Disjuntor

    O aplicativo normalmente se comunica diretamente com o armazenamento de dados persistente, mas quando o armazenamento de dados persistente tem problemas de disponibilidade, o aplicativo recupera dados do cache. Os dados podem ter sido colocados em cache usando o cache à parte ou a estratégia de envio de dados em segundo plano. Essa é uma estratégia de tratamento de falhas em vez de uma estratégia de aprimoramento de desempenho.

Para manter os dados no cache atualizados, você pode excluir entradas de cache relacionadas quando seu aplicativo cria, atualiza ou exclui dados. Se for bom que seu aplicativo às vezes obtenha dados ligeiramente desatualizados, você poderá contar com um tempo de expiração configurável para definir um limite de quão antigos os dados de cache podem ser.

Você pode configurar a expiração absoluta (quantidade de tempo desde que o item de cache foi criado) ou a expiração deslizante (quantidade de tempo desde a última vez em que um item de cache foi acessado). A expiração absoluta é usada quando você depende do mecanismo de expiração do cache para impedir que os dados se tornem muito obsoletos. No aplicativo Corrigir, removeremos manualmente itens de cache obsoletos e usaremos a expiração deslizante para manter os dados mais atuais em cache. Independentemente da política de expiração escolhida, o cache removerá automaticamente os itens mais antigos (menos usados recentemente ou LRU) quando o limite de memória do cache for atingido.

Código de cache de exemplo para o aplicativo Corrigir

No código de exemplo a seguir, marcar o cache primeiro ao recuperar uma tarefa Corrigir. Se a tarefa for encontrada no cache, a retornaremos; se não for encontrado, o obteremos do banco de dados e o armazenaremos no cache. As alterações que você faria para adicionar cache ao FindTaskByIdAsync método são realçadas.

public async Task<FixItTask> FindTaskByIdAsync(int id)
 {
    FixItTask fixItTask = null;
    Stopwatch timespan = Stopwatch.StartNew();
    string hitMiss = "Hit";

    try
    {
       fixItTask = (FixItTask)cache.Get(id.ToString());
       if (fixItTask == null)
       {
          fixItTask = await db.FixItTasks.FindAsync(id);
          cache.Put(id.ToString(), fixItTask);
          hitMiss = "Miss";
       }

       timespan.Stop();
       log.TraceApi("SQL Database", "FixItTaskRepository.FindTaskByIdAsync", timespan.Elapsed, 
                    "cache {0}, id={1}", hitMiss, id);
    }
    catch (Exception e)
    {
       log.Error(e, "Error in FixItTaskRepository.FindTaskByIdAsynx(id={0})", id);
    }

    return fixItTask;
 }

Ao atualizar ou excluir uma tarefa Corrigir, você precisa invalidar (remover) a tarefa armazenada em cache. Caso contrário, as tentativas futuras de ler essa tarefa continuarão a obter os dados antigos do cache.

public async Task UpdateAsync(FixItTask taskToSave)
{
   Stopwatch timespan = Stopwatch.StartNew();

   try
   {
      cache.Remove(taskToSave.FixItTaskId.ToString());
      db.Entry(taskToSave).State = EntityState.Modified;
      await db.SaveChangesAsync();

      timespan.Stop();
      log.TraceApi("SQL Database", "FixItTaskRepository.UpdateAsync", timespan.Elapsed, "taskToSave={0}", taskToSave);
   }
   catch (Exception e)
   {
      log.Error(e, "Error in FixItTaskRepository.UpdateAsync(taskToSave={0})", taskToSave);
   }
}

Estes são exemplos para ilustrar o código de cache simples; O cache não foi implementado no projeto Fix It para download.

Serviços de cache do Azure

O Azure oferece os seguintes serviços de cache: Cache Redis do Azure e Cache Gerenciado do Azure. O cache Redis do Azure é baseado no popular cache Redis do código aberto e é a primeira opção para a maioria dos cenários de cache.

ASP.NET estado de sessão usando um provedor de cache

Conforme mencionado no capítulo práticas recomendadas de desenvolvimento para a Web, uma prática recomendada é evitar o uso do estado de sessão. Se o aplicativo exigir estado de sessão, a próxima prática recomendada será evitar o provedor na memória padrão porque isso não habilita a expansão (várias instâncias do servidor Web). O provedor de estado de sessão ASP.NET SQL Server permite que um site executado em vários servidores Web use o estado de sessão, mas incorre em um alto custo de latência em comparação com um provedor na memória. A melhor solução se você precisar usar o estado de sessão é usar um provedor de cache, como o Provedor de Estado de Sessão para Cache do Azure.

Resumo

Você viu como o aplicativo Fix It pode implementar o cache para melhorar o tempo de resposta e a escalabilidade e permitir que o aplicativo continue respondendo para operações de leitura quando o banco de dados não estiver disponível. No próximo capítulo , mostraremos como melhorar ainda mais a escalabilidade e fazer com que o aplicativo continue respondendo para operações de gravação.

Recursos

Para obter mais informações sobre o cache, consulte os recursos a seguir.

Documentação

vídeos

  • FailSafe: criando Serviços de Nuvem escalonáveis e resilientes. Série de nove partes de Ulrich Homann, Marc Mercuri e Mark Simms. Apresenta uma exibição de 400 níveis de como arquitetar aplicativos de nuvem. Esta série se concentra na teoria e nos motivos pelos quais; para obter mais detalhes sobre como fazer isso, consulte a série Building Big de Mark Simms. Veja a discussão sobre cache no episódio 3, começando em 1:24:14.
  • Criando grandes: lições aprendidas com os clientes do Azure – Parte I. Simon Davies discute o cache distribuído a partir das 46:00. Semelhante à série Failsafe, mas entra em mais detalhes de instruções. A apresentação foi dada em 31 de outubro de 2012, portanto, não abrange o serviço de cache de Aplicativos Web em Serviço de Aplicativo do Azure que foi introduzido em 2013.

Exemplo de código