Cache distribuído no ASP.NET Core

Por Mohsin Nasir e smandia

Observação

Esta não é a versão mais recente deste artigo. Para obter a versão atual, confira a versão do ASP.NET Core 8.0 deste artigo.

Um cache distribuído é um cache compartilhado por vários servidores de aplicativos, normalmente mantido como um serviço externo para os servidores de aplicativos que o acessam. Um cache distribuído pode melhorar o desempenho e a escalabilidade de um aplicativo ASP.NET Core, especialmente quando o aplicativo é hospedado por um serviço de nuvem ou um farm de servidores.

Um cache distribuído tem várias vantagens em relação a outros cenários de cache em que os dados armazenados em cache são armazenados em servidores de aplicativos individuais.

Quando os dados armazenados em cache são distribuídos, os dados:

  • É coerente (consistente) entre solicitações para vários servidores.
  • Sobrevive a reinicializações de servidor e implantações de aplicativo.
  • Não usa memória local.

A configuração de cache distribuído é específica da implementação. Este artigo descreve como configurar caches distribuídos SQL Server e Redis. Implementações de terceiros também estão disponíveis, como NCache (NCache no GitHub). Independentemente da implementação selecionada, o aplicativo interage com o cache usando a interface IDistributedCache.

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Adicione uma referência de pacote para o provedor de cache distribuído usado:

Interface IDistributedCache

A interface IDistributedCache fornece os seguintes métodos para manipular itens na implementação do cache distribuído:

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma matriz byte[], se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como matriz byte[]) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo o tempo limite de término dinâmico (se houver).
  • Remove, RemoveAsync: Remove um item de cache com base em sua chave de cadeia de caracteres.

Estabelecer serviços de cache distribuído

Registre uma implementação de IDistributedCache em Program.cs. As implementações fornecidas pela estrutura descritas neste tópico incluem:

Cache Redis Distribuído

Recomendamos que os aplicativos de produção usem o Cache Redis Distribuído porque ele é o mais eficaz. Para obter mais informações, consulte Recomendações.

Redis é um código aberto armazenamento de dados na memória, que geralmente é usado como um cache distribuído. Você pode configurar um Cache do Azure para Redis para um aplicativo ASP.NET Core hospedado no Azure e utilizar um Cache do Azure para Redis para desenvolvimento local.

Um aplicativo configura a implementação do cache utilizando uma instância RedisCache, chamando AddStackExchangeRedisCache. Para o cache de saída, utilize AddStackExchangeRedisOutputCache.

  1. Criar um Cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuração.
    • Desenvolvimento local: Salve a cadeia de conexão com o Gerenciador de Segredos.
    • Azure: Salve a cadeia de conexão na Configuração do Serviço de Aplicativo ou em outro repositório seguro.

O código a seguir habilita o Cache do Azure para Redis:

builder.Services.AddStackExchangeRedisCache(options =>
 {
     options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");
     options.InstanceName = "SampleInstance";
 });

O código anterior pressupõe que a cadeia de conexão primária (StackExchange.Redis) foi salva na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, consulte Azure Cache for Redis.

Consulte este problema do GitHub para obter uma discussão sobre abordagens alternativas para um cache Redis local.

Cache de Memória Distribuída

O Cache de Memória Distribuída (AddDistributedMemoryCache) é uma implementação fornecida pela estrutura de IDistributedCache que armazena itens na memória. O Cache de Memória Distribuída não é um cache distribuído real. Os itens armazenados em cache são armazenados pela instância do aplicativo no servidor em que o aplicativo está em execução.

O Cache de Memória Distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado em produção e o consumo de memória não é um problema. Implementar o Cache de Memória Distribuída abstrai o armazenamento de dados armazenado em cache. Ele permite implementar uma solução de cache distribuída verdadeira no futuro se vários nós ou tolerância a falhas se tornarem necessários.

O aplicativo de exemplo usa o Cache de Memória Distribuída quando o aplicativo é executado no ambiente de desenvolvimento em Program.cs:

builder.Services.AddDistributedMemoryCache();

Cache de SQL Server distribuído

A implementação do Cache de SQL Server Distribuído (AddDistributedSqlServerCache) permite que o cache distribuído use um banco de dados SQL Server como repositório de backup. Para criar um SQL Server tabela de itens armazenados em cache em uma instância de SQL Server, você pode usar a ferramenta sql-cache. A ferramenta cria uma tabela com o nome e o esquema especificados.

Crie uma tabela no SQL Server executando o comando sql-cache create. Forneça a instância de SQL Server (Data Source), o banco de dados (Initial Catalog), o esquema (por exemplo, dbo) e o nome da tabela (por exemplo, TestCache):

dotnet sql-cache create "Data Source=(localdb)/MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

Uma mensagem é registrada para indicar que a ferramenta foi bem-sucedida:

Table and index were created successfully.

A tabela criada pela ferramenta sql-cache tem o seguinte esquema:

SqlServer Cache Table

Observação

Um aplicativo deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

O aplicativo de exemplo implementa SqlServerCache em um ambiente que não seja de desenvolvimento em Program.cs:

builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString(
        "DistCache_ConnectionString");
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

Observação

Um ConnectionString (e, opcionalmente, SchemaName e TableName) normalmente são armazenados fora do controle do código-fonte (por exemplo, armazenados pelo Gerenciador de Segredos ou em arquivos appsettings.json/appsettings.{Environment}.json). A cadeia de conexão pode conter credenciais que devem ser mantidas fora dos sistemas de controle do código-fonte.

Cache NCache distribuído

NCache é um cache distribuído código aberto na memória desenvolvido nativamente no .NET e no .NET Core. O NCache funciona localmente e configurado como um cluster de cache distribuído para um aplicativo ASP.NET Core em execução no Azure ou em outras plataformas de hospedagem.

Para instalar e configurar o NCache em seu computador local, consulte Guia de Introdução para Windows (.NET e .NET Core).

Para configurar o NCache:

  1. Instale o código aberto NuGet do NCache.
  2. Configure o cluster de cache em client.ncconf.
  3. Adicione o seguinte código a Program.cs:
builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Cache distribuído do Azure CosmosDB

O Azure Cosmos DB pode ser usado no ASP.NET Core como um provedor de estado de sessão por meio da interface IDistributedCache. O Azure Cosmos DB é um banco de dados relacional e NoSQL totalmente gerenciado para desenvolvimento de aplicativos modernos que oferece alta disponibilidade, escalabilidade e acesso de baixa latência a dados para aplicativos críticos.

Depois de instalar o pacote NuGet Microsoft.Extensions.Caching.Cosmos, configure um cache distribuído do Azure Cosmos DB da seguinte maneira:

Reutilizar um cliente existente

A maneira mais fácil de configurar o cache distribuído é reutilizando um cliente existente do Azure Cosmos DB. Nesse caso, a instância CosmosClient não será descartada quando o provedor for descartado.

services.AddCosmosCache((CosmosCacheOptions cacheOptions) =>
{
    cacheOptions.ContainerName = Configuration["CosmosCacheContainer"];
    cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"];
    cacheOptions.CosmosClient = existingCosmosClient;
    cacheOptions.CreateIfNotExists = true;
});

Criar um cliente

Como alternativa, crie uma instância de um cliente. Nesse caso, a instância CosmosClient será descartada quando o provedor for descartado.

services.AddCosmosCache((CosmosCacheOptions cacheOptions) =>
{
    cacheOptions.ContainerName = Configuration["CosmosCacheContainer"];
    cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"];
    cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]);
    cacheOptions.CreateIfNotExists = true;
});

Usar o cache distribuído

Para usar a interface IDistributedCache, solicite uma instância do IDistributedCache no aplicativo. A instância é fornecida por DI (injeção de dependência).

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado em Program.cs. A hora atual é armazenada em cache usando IHostApplicationLifetime (para obter mais informações, consulte Host Genérico: IHostApplicationLifetime):

app.Lifetime.ApplicationStarted.Register(() =>
{
    var currentTimeUTC = DateTime.UtcNow.ToString();
    byte[] encodedCurrentTimeUTC = System.Text.Encoding.UTF8.GetBytes(currentTimeUTC);
    var options = new DistributedCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromSeconds(20));
    app.Services.GetService<IDistributedCache>()
                              .Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
});

O aplicativo de exemplo injeta IDistributedCache no IndexModel para uso pela página Índice.

Sempre que a página Índice é carregada, o cache é verificado quanto ao tempo armazenado em cache em OnGetAsync. Se o tempo armazenado em cache não tiver expirado, a hora será exibida. Se 20 segundos tiverem decorrido desde a última vez em que a hora armazenada em cache foi acessada (a última vez em que esta página foi carregada), a página exibirá Hora Armazenada em Cache Expirada.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir Hora Armazenada em Cache. O botão dispara o método de manipulador OnPostResetCachedTime.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string? CachedTimeUTC { get; set; }
    public string? ASP_Environment { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }

        ASP_Environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        if (String.IsNullOrEmpty(ASP_Environment))
        {
            ASP_Environment = "Null, so Production";
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

Não é necessário usar um tempo de vida Singleton ou com Escopo para instâncias IDistributedCache com as implementações internas.

Você também pode criar uma instância IDistributedCache onde quer que precise de uma em vez de usar a DI, mas criar uma instância no código pode tornar seu código mais difícil de testar e violar o Princípio de Dependências Explícitas.

Recomendações

Ao decidir qual implementação de IDistributedCache é melhor para seu aplicativo, considere o seguinte:

  • Infraestrutura existente
  • Requisitos de desempenho
  • Custo
  • Experiência em equipe

As soluções de cache geralmente dependem do armazenamento na memória para fornecer recuperação rápida de dados armazenados em cache, mas a memória é um recurso limitado e dispendido para expandir. Armazene apenas dados comumente usados em um cache.

Para a maioria dos aplicativos, um cache Redis fornece maior taxa de transferência e menor latência do que um cache de SQL Server. No entanto, é recomendável fazer um parâmetro de comparação para determinar as características de desempenho das estratégias de cache.

Quando SQL Server é usado como um repositório de backup de cache distribuído, o uso do mesmo banco de dados para o cache e o armazenamento e recuperação de dados comuns do aplicativo podem afetar negativamente o desempenho de ambos. É recomendável usar uma instância de SQL Server dedicada para o repositório de backup de cache distribuído.

Recursos adicionais

Um cache distribuído é um cache compartilhado por vários servidores de aplicativos, normalmente mantido como um serviço externo para os servidores de aplicativos que o acessam. Um cache distribuído pode melhorar o desempenho e a escalabilidade de um aplicativo ASP.NET Core, especialmente quando o aplicativo é hospedado por um serviço de nuvem ou um farm de servidores.

Um cache distribuído tem várias vantagens em relação a outros cenários de cache em que os dados armazenados em cache são armazenados em servidores de aplicativos individuais.

Quando os dados armazenados em cache são distribuídos, os dados:

  • É coerente (consistente) entre solicitações para vários servidores.
  • Sobrevive a reinicializações de servidor e implantações de aplicativo.
  • Não usa memória local.

A configuração de cache distribuído é específica da implementação. Este artigo descreve como configurar caches distribuídos SQL Server e Redis. Implementações de terceiros também estão disponíveis, como NCache (NCache no GitHub). Independentemente da implementação selecionada, o aplicativo interage com o cache usando a interface IDistributedCache.

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Adicione uma referência de pacote para o provedor de cache distribuído usado:

Interface IDistributedCache

A interface IDistributedCache fornece os seguintes métodos para manipular itens na implementação do cache distribuído:

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma matriz byte[], se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como matriz byte[]) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo o tempo limite de término dinâmico (se houver).
  • Remove, RemoveAsync: Remove um item de cache com base em sua chave de cadeia de caracteres.

Estabelecer serviços de cache distribuído

Registre uma implementação de IDistributedCache em Program.cs. As implementações fornecidas pela estrutura descritas neste tópico incluem:

Cache Redis Distribuído

Recomendamos que os aplicativos de produção usem o Cache Redis Distribuído porque ele é o mais eficaz. Para obter mais informações, consulte Recomendações.

Redis é um código aberto armazenamento de dados na memória, que geralmente é usado como um cache distribuído. Você pode configurar um Cache Redis do Azure para um aplicativo de ASP.NET Core hospedado no Azure e usar um Cache Redis do Azure para desenvolvimento local.

Um aplicativo configura a implementação do cache usando uma instância RedisCache (AddStackExchangeRedisCache).

  1. Criar um Cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuração.
    • Desenvolvimento local: Salve a cadeia de conexão com o Gerenciador de Segredos.
    • Azure: Salve a cadeia de conexão na Configuração do Serviço de Aplicativo ou em outro repositório seguro.

O código a seguir habilita o Cache do Azure para Redis:

builder.Services.AddStackExchangeRedisCache(options =>
 {
     options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");
     options.InstanceName = "SampleInstance";
 });

O código anterior pressupõe que a cadeia de conexão primária (StackExchange.Redis) foi salva na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, consulte Azure Cache for Redis.

Consulte este problema do GitHub para obter uma discussão sobre abordagens alternativas para um cache Redis local.

Cache de Memória Distribuída

O Cache de Memória Distribuída (AddDistributedMemoryCache) é uma implementação fornecida pela estrutura de IDistributedCache que armazena itens na memória. O Cache de Memória Distribuída não é um cache distribuído real. Os itens armazenados em cache são armazenados pela instância do aplicativo no servidor em que o aplicativo está em execução.

O Cache de Memória Distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado em produção e o consumo de memória não é um problema. Implementar o Cache de Memória Distribuída abstrai o armazenamento de dados armazenado em cache. Ele permite implementar uma solução de cache distribuída verdadeira no futuro se vários nós ou tolerância a falhas se tornarem necessários.

O aplicativo de exemplo usa o Cache de Memória Distribuída quando o aplicativo é executado no ambiente de desenvolvimento em Program.cs:

builder.Services.AddDistributedMemoryCache();

Cache de SQL Server distribuído

A implementação do Cache de SQL Server Distribuído (AddDistributedSqlServerCache) permite que o cache distribuído use um banco de dados SQL Server como repositório de backup. Para criar um SQL Server tabela de itens armazenados em cache em uma instância de SQL Server, você pode usar a ferramenta sql-cache. A ferramenta cria uma tabela com o nome e o esquema especificados.

Crie uma tabela no SQL Server executando o comando sql-cache create. Forneça a instância de SQL Server (Data Source), o banco de dados (Initial Catalog), o esquema (por exemplo, dbo) e o nome da tabela (por exemplo, TestCache):

dotnet sql-cache create "Data Source=(localdb)/MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

Uma mensagem é registrada para indicar que a ferramenta foi bem-sucedida:

Table and index were created successfully.

A tabela criada pela ferramenta sql-cache tem o seguinte esquema:

SqlServer Cache Table

Observação

Um aplicativo deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

O aplicativo de exemplo implementa SqlServerCache em um ambiente que não seja de desenvolvimento em Program.cs:

builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString(
        "DistCache_ConnectionString");
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

Observação

Um ConnectionString (e, opcionalmente, SchemaName e TableName) normalmente são armazenados fora do controle do código-fonte (por exemplo, armazenados pelo Gerenciador de Segredos ou em arquivos appsettings.json/appsettings.{Environment}.json). A cadeia de conexão pode conter credenciais que devem ser mantidas fora dos sistemas de controle do código-fonte.

Cache NCache distribuído

NCache é um cache distribuído código aberto na memória desenvolvido nativamente no .NET e no .NET Core. O NCache funciona localmente e configurado como um cluster de cache distribuído para um aplicativo ASP.NET Core em execução no Azure ou em outras plataformas de hospedagem.

Para instalar e configurar o NCache em seu computador local, consulte Guia de Introdução para Windows (.NET e .NET Core).

Para configurar o NCache:

  1. Instale o código aberto NuGet do NCache.
  2. Configure o cluster de cache em client.ncconf.
  3. Adicione o seguinte código a Program.cs:
builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Usar o cache distribuído

Para usar a interface IDistributedCache, solicite uma instância do IDistributedCache no aplicativo. A instância é fornecida por DI (injeção de dependência).

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado em Program.cs. A hora atual é armazenada em cache usando IHostApplicationLifetime (para obter mais informações, consulte Host Genérico: IHostApplicationLifetime):

app.Lifetime.ApplicationStarted.Register(() =>
{
    var currentTimeUTC = DateTime.UtcNow.ToString();
    byte[] encodedCurrentTimeUTC = System.Text.Encoding.UTF8.GetBytes(currentTimeUTC);
    var options = new DistributedCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromSeconds(20));
    app.Services.GetService<IDistributedCache>()
                              .Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
});

O aplicativo de exemplo injeta IDistributedCache no IndexModel para uso pela página Índice.

Sempre que a página Índice é carregada, o cache é verificado quanto ao tempo armazenado em cache em OnGetAsync. Se o tempo armazenado em cache não tiver expirado, a hora será exibida. Se 20 segundos tiverem decorrido desde a última vez em que a hora armazenada em cache foi acessada (a última vez em que esta página foi carregada), a página exibirá Hora Armazenada em Cache Expirada.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir Hora Armazenada em Cache. O botão dispara o método de manipulador OnPostResetCachedTime.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string? CachedTimeUTC { get; set; }
    public string? ASP_Environment { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }

        ASP_Environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        if (String.IsNullOrEmpty(ASP_Environment))
        {
            ASP_Environment = "Null, so Production";
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

Não é necessário usar um tempo de vida Singleton ou com Escopo para instâncias IDistributedCache com as implementações internas.

Você também pode criar uma instância IDistributedCache onde quer que precise de uma em vez de usar a DI, mas criar uma instância no código pode tornar seu código mais difícil de testar e violar o Princípio de Dependências Explícitas.

Recomendações

Ao decidir qual implementação de IDistributedCache é melhor para seu aplicativo, considere o seguinte:

  • Infraestrutura existente
  • Requisitos de desempenho
  • Custo
  • Experiência em equipe

As soluções de cache geralmente dependem do armazenamento na memória para fornecer recuperação rápida de dados armazenados em cache, mas a memória é um recurso limitado e dispendido para expandir. Armazene apenas dados comumente usados em um cache.

Para a maioria dos aplicativos, um cache Redis fornece maior taxa de transferência e menor latência do que um cache de SQL Server. No entanto, é recomendável fazer um parâmetro de comparação para determinar as características de desempenho das estratégias de cache.

Quando SQL Server é usado como um repositório de backup de cache distribuído, o uso do mesmo banco de dados para o cache e o armazenamento e recuperação de dados comuns do aplicativo podem afetar negativamente o desempenho de ambos. É recomendável usar uma instância de SQL Server dedicada para o repositório de backup de cache distribuído.

Recursos adicionais

Um cache distribuído é um cache compartilhado por vários servidores de aplicativos, normalmente mantido como um serviço externo para os servidores de aplicativos que o acessam. Um cache distribuído pode melhorar o desempenho e a escalabilidade de um aplicativo ASP.NET Core, especialmente quando o aplicativo é hospedado por um serviço de nuvem ou um farm de servidores.

Um cache distribuído tem várias vantagens em relação a outros cenários de cache em que os dados armazenados em cache são armazenados em servidores de aplicativos individuais.

Quando os dados armazenados em cache são distribuídos, os dados:

  • É coerente (consistente) entre solicitações para vários servidores.
  • Sobrevive a reinicializações de servidor e implantações de aplicativo.
  • Não usa memória local.

A configuração de cache distribuído é específica da implementação. Este artigo descreve como configurar caches distribuídos SQL Server e Redis. Implementações de terceiros também estão disponíveis, como NCache (NCache no GitHub). Independentemente da implementação selecionada, o aplicativo interage com o cache usando a interface IDistributedCache.

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Para usar um cache distribuído do SQL Server, adicione uma referência de pacote ao pacote Microsoft.Extensions.Caching.SqlServer.

Para usar um cache distribuído do Redis, adicione uma referência de pacote ao pacote Microsoft.Extensions.Caching.StackExchangeRedis.

Para usar o cache distribuído do NCache, adicione uma referência de pacote ao pacote NCache.Microsoft.Extensions.Caching.OpenSource.

Interface IDistributedCache

A interface IDistributedCache fornece os seguintes métodos para manipular itens na implementação do cache distribuído:

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma matriz byte[], se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como matriz byte[]) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo o tempo limite de término dinâmico (se houver).
  • Remove, RemoveAsync: Remove um item de cache com base em sua chave de cadeia de caracteres.

Estabelecer serviços de cache distribuído

Registre uma implementação de IDistributedCache em Startup.ConfigureServices. As implementações fornecidas pela estrutura descritas neste tópico incluem:

Cache de Memória Distribuída

O Cache de Memória Distribuída (AddDistributedMemoryCache) é uma implementação fornecida pela estrutura de IDistributedCache que armazena itens na memória. O Cache de Memória Distribuída não é um cache distribuído real. Os itens armazenados em cache são armazenados pela instância do aplicativo no servidor em que o aplicativo está em execução.

O Cache de Memória Distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado em produção e o consumo de memória não é um problema. Implementar o Cache de Memória Distribuída abstrai o armazenamento de dados armazenado em cache. Ele permite implementar uma solução de cache distribuída verdadeira no futuro se vários nós ou tolerância a falhas se tornarem necessários.

O aplicativo de exemplo usa o Cache de Memória Distribuída quando o aplicativo é executado no ambiente de desenvolvimento em Startup.ConfigureServices:

services.AddDistributedMemoryCache();

Cache de SQL Server distribuído

A implementação do Cache de SQL Server Distribuído (AddDistributedSqlServerCache) permite que o cache distribuído use um banco de dados SQL Server como repositório de backup. Para criar um SQL Server tabela de itens armazenados em cache em uma instância de SQL Server, você pode usar a ferramenta sql-cache. A ferramenta cria uma tabela com o nome e o esquema especificados.

Crie uma tabela no SQL Server executando o comando sql-cache create. Forneça a instância de SQL Server (Data Source), o banco de dados (Initial Catalog), o esquema (por exemplo, dbo) e o nome da tabela (por exemplo, TestCache):

dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

Uma mensagem é registrada para indicar que a ferramenta foi bem-sucedida:

Table and index were created successfully.

A tabela criada pela ferramenta sql-cache tem o seguinte esquema:

SqlServer Cache Table

Observação

Um aplicativo deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

O aplicativo de exemplo implementa SqlServerCache em um ambiente que não seja de desenvolvimento em Startup.ConfigureServices:

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = 
        _config["DistCache_ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

Observação

Um ConnectionString (e, opcionalmente, SchemaName e TableName) normalmente são armazenados fora do controle do código-fonte (por exemplo, armazenados pelo Gerenciador de Segredos ou em arquivos appsettings.json/appsettings.{Environment}.json). A cadeia de conexão pode conter credenciais que devem ser mantidas fora dos sistemas de controle do código-fonte.

Cache Redis Distribuído

Redis é um código aberto armazenamento de dados na memória, que geralmente é usado como um cache distribuído. Você pode configurar um Cache Redis do Azure para um aplicativo de ASP.NET Core hospedado no Azure e usar um Cache Redis do Azure para desenvolvimento local.

Um aplicativo configura a implementação do cache usando uma instância RedisCache (AddStackExchangeRedisCache).

  1. Criar um Cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuração.
    • Desenvolvimento local: Salve a cadeia de conexão com o Gerenciador de Segredos.
    • Azure: Salve a cadeia de conexão na Configuração do Serviço de Aplicativo ou em outro repositório seguro.

O código a seguir habilita o Cache do Azure para Redis:

public void ConfigureServices(IServiceCollection services)
{
    if (_hostContext.IsDevelopment())
    {
        services.AddDistributedMemoryCache();
    }
    else
    {
        services.AddStackExchangeRedisCache(options =>
        {
            options.Configuration = _config["MyRedisConStr"];
            options.InstanceName = "SampleInstance";
        });
    }

    services.AddRazorPages();
}

O código anterior pressupõe que a cadeia de conexão primária (StackExchange.Redis) foi salva na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, consulte Azure Cache for Redis.

Consulte este problema do GitHub para obter uma discussão sobre abordagens alternativas para um cache Redis local.

Cache NCache distribuído

NCache é um cache distribuído código aberto na memória desenvolvido nativamente no .NET e no .NET Core. O NCache funciona localmente e configurado como um cluster de cache distribuído para um aplicativo ASP.NET Core em execução no Azure ou em outras plataformas de hospedagem.

Para instalar e configurar o NCache em seu computador local, consulte Guia de Introdução para Windows (.NET e .NET Core).

Para configurar o NCache:

  1. Instale o código aberto NuGet do NCache.

  2. Configure o cluster de cache em client.ncconf.

  3. Adicione o seguinte código a Startup.ConfigureServices:

    services.AddNCacheDistributedCache(configuration =>    
    {        
        configuration.CacheName = "demoClusteredCache";
        configuration.EnableLogs = true;
        configuration.ExceptionsEnabled = true;
    });
    

Usar o cache distribuído

Para usar a interface IDistributedCache, solicite uma instância de IDistributedCache de qualquer construtor no aplicativo. A instância é fornecida por DI (injeção de dependência).

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado em Startup.Configure. A hora atual é armazenada em cache usando IHostApplicationLifetime (para obter mais informações, consulte Host Genérico: IHostApplicationLifetime):

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    IHostApplicationLifetime lifetime, IDistributedCache cache)
{
    lifetime.ApplicationStarted.Register(() =>
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    });

O aplicativo de exemplo injeta IDistributedCache no IndexModel para uso pela página Índice.

Sempre que a página Índice é carregada, o cache é verificado quanto ao tempo armazenado em cache em OnGetAsync. Se o tempo armazenado em cache não tiver expirado, a hora será exibida. Se 20 segundos tiverem decorrido desde a última vez em que a hora armazenada em cache foi acessada (a última vez em que esta página foi carregada), a página exibirá Hora Armazenada em Cache Expirada.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir Hora Armazenada em Cache. O botão dispara o método de manipulador OnPostResetCachedTime.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string CachedTimeUTC { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

Observação

Não é necessário usar um tempo de vida Singleton ou com Escopo para instâncias IDistributedCache (pelo menos para as implementações internas).

Você também pode criar uma instância IDistributedCache onde quer que precise de uma em vez de usar a DI, mas criar uma instância no código pode tornar seu código mais difícil de testar e violar o Princípio de Dependências Explícitas.

Recomendações

Ao decidir qual implementação de IDistributedCache é melhor para seu aplicativo, considere o seguinte:

  • Infraestrutura existente
  • Requisitos de desempenho
  • Custo
  • Experiência em equipe

As soluções de cache geralmente dependem do armazenamento na memória para fornecer recuperação rápida de dados armazenados em cache, mas a memória é um recurso limitado e dispendido para expandir. Armazene apenas dados comumente usados em um cache.

Geralmente, um cache Redis fornece maior taxa de transferência e latência menor do que um cache SQL Server. No entanto, o parâmetro de comparação geralmente é necessário para determinar as características de desempenho das estratégias de cache.

Quando SQL Server é usado como um repositório de backup de cache distribuído, o uso do mesmo banco de dados para o cache e o armazenamento e recuperação de dados comuns do aplicativo podem afetar negativamente o desempenho de ambos. É recomendável usar uma instância de SQL Server dedicada para o repositório de backup de cache distribuído.

Recursos adicionais