Partilhar via


Cache distribuído no ASP.NET Core

Por Mohsin Nasir e smandia

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Um cache distribuído é um cache compartilhado por vários servidores de aplicativos, normalmente mantido como um serviço externo aos 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 servidores e implantações de aplicativos.
  • Não usa memória local.

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

Visualizar ou descarregar amostra de código (como descarregar)

Warning

Este artigo usa um banco de dados local que não exige que o usuário seja autenticado. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos de teste e produção implantados, consulte Fluxos de autenticação seguros.

Prerequisites

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

Interface IDistributedCache

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

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma byte[] matriz, se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como byte[] matriz) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo seu tempo limite de expiração deslizante (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

Registrar 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 eficiente. Para obter mais informações, consulte Recomendações.

O Redis é um armazenamento de dados na memória de código aberto, que é frequentemente 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 usar um Cache do Azure para Redis para desenvolvimento local.

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

  1. Crie um cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuration.
    • Desenvolvimento local: Salve a cadeia de conexão com o Secret Manager.
    • Azure: Salve a cadeia de conexão em um repositório seguro, como o Azure Key Vault

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 guardada na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, veja Azure Cache for Redis (Cache do Azure para Redis).

Consulte esta questão do GitHub para uma discussão sobre abordagens alternativas a um cache Redis local.

Cache de memória distribuída

O Cache de Memória Distribuída (AddDistributedMemoryCache) é uma implementação fornecida pela estrutura 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á sendo executado.

O cache de memória distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado na produção e o consumo de memória não é um problema. A implementação do Cache de Memória Distribuída abstrai o armazenamento de dados em cache. Ele permite a implementação de uma verdadeira solução de cache distribuído no futuro, caso vários nós ou a necessidade de tolerância a falhas se tornem 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 distribuído do SQL Server

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

Crie uma tabela no SQL Server executando o sql-cache create comando. Forneça a instância do 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 sql-cache ferramenta tem o seguinte esquema:

Tabela de cache do SqlServer

Note

Uma aplicação deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

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

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

Note

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

Cache Postgres distribuído

O Banco de Dados do Azure para PostgreSQL pode ser usado como um armazenamento de backup de cache distribuído por meio da IDistributedCache interface. O Banco de Dados do Azure para PostgreSQL é uma oferta de Banco de Dados como Serviço (DBaaS) totalmente gerenciada e pronta para IA criada no mecanismo PostgreSQL de código aberto, projetada para dar suporte a cargas de trabalho de missão crítica com desempenho previsível, segurança robusta, alta disponibilidade e escalabilidade perfeita.

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

  1. Registar o Serviço
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Register Postgres distributed cache
builder.Services.AddDistributedPostgresCache(options => {
    options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
    options.SchemaName = builder.Configuration.GetValue<string>("PostgresCache:SchemaName", "public");
    options.TableName = builder.Configuration.GetValue<string>("PostgresCache:TableName", "cache");
    options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists", true);
    options.UseWAL = builder.Configuration.GetValue<bool>("PostgresCache:UseWAL", false);

    // Optional: Configure expiration settings

    var expirationInterval = builder.Configuration.GetValue<string>("PostgresCache:ExpiredItemsDeletionInterval");
    if (!string.IsNullOrEmpty(expirationInterval) && TimeSpan.TryParse(expirationInterval, out var interval)) {
        options.ExpiredItemsDeletionInterval = interval;
    }

    var slidingExpiration = builder.Configuration.GetValue<string>("PostgresCache:DefaultSlidingExpiration");
    if (!string.IsNullOrEmpty(slidingExpiration) && TimeSpan.TryParse(slidingExpiration, out var sliding)) {
        options.DefaultSlidingExpiration = sliding;
    }
});

var app = builder.Build();
  1. Usar o cache
public class MyService {
    private readonly IDistributedCache _cache; 

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

    public async Task<string> GetDataAsync(string key) {
        var cachedData = await _cache.GetStringAsync(key);

        if (cachedData == null) {
            // Fetch data from source
            var data = await FetchDataFromSource();

            // Cache the data with options
            var options = new DistributedCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
                SlidingExpiration = TimeSpan.FromMinutes(5)
            };

            await _cache.SetStringAsync(key, data, options);
            return data;
        }

        return cachedData;
    }
}

Cache distribuído NCache

NCache é um cache distribuído em memória de código aberto desenvolvido nativamente em .NET. 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 sua máquina local, consulte Guia de Introdução para Windows & Linux.

Para configurar o NCache:

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

Cache distribuído do Azure Cosmos DB

O Azure Cosmos DB pode ser usado no ASP.NET Core como um provedor de estado de sessão usando a IDistributedCache interface. 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 de missão crítica.

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 novo cliente

Como alternativa, instancie um novo 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 IDistributedCache interface, solicite uma instância de IDistributedCache no aplicativo. A instância é fornecida por injeção de dependência (DI).

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado no 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.

Cada vez que a página Índice é carregada, o cache é verificado para o tempo armazenado em cache no OnGetAsync. Se o tempo armazenado em cache não tiver expirado, o tempo será exibido. Se tiverem decorrido 20 segundos desde a última vez que a hora em cache foi acedida (a última vez que esta página foi carregada), a página apresenta Tempo em Cache Expirado.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir hora em cache . O botão aciona o método 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 há necessidade de usar um ciclo de vida Singleton ou Scoped para IDistributedCache instâncias com as implementações incorporadas.

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

Recommendations

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

  • Infraestrutura existente
  • Requisitos de desempenho
  • Cost
  • Experiência de equipa

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 caro para expandir. Armazene apenas os dados mais 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 do SQL Server. No entanto, recomenda-se o benchmarking para determinar as características de desempenho das estratégias de cache.

Quando o SQL Server é usado como um armazenamento 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. Recomendamos o uso de uma instância dedicada do SQL Server para o armazenamento 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 aos 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 servidores e implantações de aplicativos.
  • Não usa memória local.

A configuração do cache distribuído é específica da implementação. Este artigo descreve como configurar caches distribuídos do SQL Server, Redis e Postgres. 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 IDistributedCache interface.

Visualizar ou descarregar amostra de código (como descarregar)

Prerequisites

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

Interface IDistributedCache

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

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma byte[] matriz, se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como byte[] matriz) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo seu tempo limite de expiração deslizante (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

Registrar 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 eficiente. Para obter mais informações, consulte Recomendações.

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

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

  1. Crie um cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuration.
    • Desenvolvimento local: Salve a cadeia de conexão com o Secret Manager.
    • Azure: Salve a cadeia de conexão em um repositório seguro, como o Azure Key Vault

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 guardada na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, veja Azure Cache for Redis (Cache do Azure para Redis).

Consulte esta questão do GitHub para uma discussão sobre abordagens alternativas a um cache Redis local.

Cache de memória distribuída

O Cache de Memória Distribuída (AddDistributedMemoryCache) é uma implementação fornecida pela estrutura 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á sendo executado.

O cache de memória distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado na produção e o consumo de memória não é um problema. A implementação do Cache de Memória Distribuída abstrai o armazenamento de dados em cache. Ele permite a implementação de uma verdadeira solução de cache distribuído no futuro, caso vários nós ou a necessidade de tolerância a falhas se tornem 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 distribuído do SQL Server

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

Crie uma tabela no SQL Server executando o sql-cache create comando. Forneça a instância do 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 sql-cache ferramenta tem o seguinte esquema:

Tabela de cache do SqlServer

Note

Uma aplicação deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

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

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

Note

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

Cache Postgres distribuído

O Banco de Dados do Azure para PostgreSQL pode ser usado como um armazenamento de backup de cache distribuído por meio da IDistributedCache interface. O Banco de Dados do Azure para PostgreSQL é uma oferta de Banco de Dados como Serviço (DBaaS) totalmente gerenciada e pronta para IA criada no mecanismo PostgreSQL de código aberto, projetada para dar suporte a cargas de trabalho de missão crítica com desempenho previsível, segurança robusta, alta disponibilidade e escalabilidade perfeita.

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

  1. Registar o Serviço
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Register Postgres distributed cache
builder.Services.AddDistributedPostgresCache(options => {
    options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
    options.SchemaName = builder.Configuration.GetValue<string>("PostgresCache:SchemaName", "public");
    options.TableName = builder.Configuration.GetValue<string>("PostgresCache:TableName", "cache");
    options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists", true);
    options.UseWAL = builder.Configuration.GetValue<bool>("PostgresCache:UseWAL", false);

    // Optional: Configure expiration settings

    var expirationInterval = builder.Configuration.GetValue<string>("PostgresCache:ExpiredItemsDeletionInterval");
    if (!string.IsNullOrEmpty(expirationInterval) && TimeSpan.TryParse(expirationInterval, out var interval)) {
        options.ExpiredItemsDeletionInterval = interval;
    }

    var slidingExpiration = builder.Configuration.GetValue<string>("PostgresCache:DefaultSlidingExpiration");
    if (!string.IsNullOrEmpty(slidingExpiration) && TimeSpan.TryParse(slidingExpiration, out var sliding)) {
        options.DefaultSlidingExpiration = sliding;
    }
});

var app = builder.Build();
  1. Usar o cache
public class MyService {
    private readonly IDistributedCache _cache; 

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

    public async Task<string> GetDataAsync(string key) {
        var cachedData = await _cache.GetStringAsync(key);

        if (cachedData == null) {
            // Fetch data from source
            var data = await FetchDataFromSource();

            // Cache the data with options
            var options = new DistributedCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
                SlidingExpiration = TimeSpan.FromMinutes(5)
            };

            await _cache.SetStringAsync(key, data, options);
            return data;
        }

        return cachedData;
    }
}

Cache distribuído NCache

NCache é um cache distribuído em memória de código aberto desenvolvido nativamente em .NET e .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 sua máquina local, consulte Guia de Introdução para Windows (.NET e .NET Core).

Para configurar o NCache:

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

Usar o cache distribuído

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

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado no 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.

Cada vez que a página Índice é carregada, o cache é verificado para o tempo armazenado em cache no OnGetAsync. Se o tempo armazenado em cache não tiver expirado, o tempo será exibido. Se tiverem decorrido 20 segundos desde a última vez que a hora em cache foi acedida (a última vez que esta página foi carregada), a página apresenta Tempo em Cache Expirado.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir hora em cache . O botão aciona o método 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 há necessidade de usar um ciclo de vida Singleton ou Scoped para IDistributedCache instâncias com as implementações incorporadas.

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

Recommendations

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

  • Infraestrutura existente
  • Requisitos de desempenho
  • Cost
  • Experiência de equipa

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 caro para expandir. Armazene apenas os dados mais 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 do SQL Server. No entanto, recomenda-se o benchmarking para determinar as características de desempenho das estratégias de cache.

Quando o SQL Server é usado como um armazenamento 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. Recomendamos o uso de uma instância dedicada do SQL Server para o armazenamento 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 aos 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 servidores e implantações de aplicativos.
  • Não usa memória local.

A configuração do cache distribuído é específica da implementação. Este artigo descreve como configurar caches distribuídos do SQL Server, Redis e Postgres. 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 IDistributedCache interface.

Visualizar ou descarregar amostra de código (como descarregar)

Prerequisites

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 Redis, adicione uma referência de pacote ao pacote Microsoft.Extensions.Caching.StackExchangeRedis .

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

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

Interface IDistributedCache

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

  • Get, GetAsync: Aceita uma chave de cadeia de caracteres e recupera um item armazenado em cache como uma byte[] matriz, se encontrado no cache.
  • Set, SetAsync: Adiciona um item (como byte[] matriz) ao cache usando uma chave de cadeia de caracteres.
  • Refresh, RefreshAsync: Atualiza um item no cache com base em sua chave, redefinindo seu tempo limite de expiração deslizante (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

Registrar 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 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á sendo executado.

O cache de memória distribuída é uma implementação útil:

  • Em cenários de desenvolvimento e teste.
  • Quando um único servidor é usado na produção e o consumo de memória não é um problema. A implementação do Cache de Memória Distribuída abstrai o armazenamento de dados em cache. Ele permite a implementação de uma verdadeira solução de cache distribuído no futuro, caso vários nós ou a necessidade de tolerância a falhas se tornem 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 distribuído do SQL Server

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

Crie uma tabela no SQL Server executando o sql-cache create comando. Forneça a instância do 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 sql-cache ferramenta tem o seguinte esquema:

Tabela de cache do SqlServer

Note

Uma aplicação deve manipular valores de cache usando uma instância de IDistributedCache, não um SqlServerCache.

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

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

Note

A ConnectionString (e, opcionalmente, SchemaName e TableName) são normalmente armazenados fora do controle do código-fonte (por exemplo, armazenados pelo Gerenciador Secreto ou em appsettings.json/appsettings.{Environment}.json arquivos). 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

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

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

  1. Crie um cache do Azure para Redis.
  2. Copie a cadeia de conexão primária (StackExchange.Redis) para Configuration.
    • Desenvolvimento local: Salve a cadeia de conexão com o Secret Manager.
    • Azure: Salve a cadeia de conexão em um repositório seguro, como o Azure Key Vault

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 guardada na configuração com o nome da chave MyRedisConStr.

Para obter mais informações, veja Azure Cache for Redis (Cache do Azure para Redis).

Consulte esta questão do GitHub para uma discussão sobre abordagens alternativas a um cache Redis local.

Cache Postgres distribuído

O Banco de Dados do Azure para PostgreSQL pode ser usado como um armazenamento de backup de cache distribuído por meio da IDistributedCache interface. O Banco de Dados do Azure para PostgreSQL é uma oferta de Banco de Dados como Serviço (DBaaS) totalmente gerenciada e pronta para IA criada no mecanismo PostgreSQL de código aberto, projetada para dar suporte a cargas de trabalho de missão crítica com desempenho previsível, segurança robusta, alta disponibilidade e escalabilidade perfeita.

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

  1. Registar o Serviço
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Register Postgres distributed cache
builder.Services.AddDistributedPostgresCache(options => {
    options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
    options.SchemaName = builder.Configuration.GetValue<string>("PostgresCache:SchemaName", "public");
    options.TableName = builder.Configuration.GetValue<string>("PostgresCache:TableName", "cache");
    options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists", true);
    options.UseWAL = builder.Configuration.GetValue<bool>("PostgresCache:UseWAL", false);

    // Optional: Configure expiration settings

    var expirationInterval = builder.Configuration.GetValue<string>("PostgresCache:ExpiredItemsDeletionInterval");
    if (!string.IsNullOrEmpty(expirationInterval) && TimeSpan.TryParse(expirationInterval, out var interval)) {
        options.ExpiredItemsDeletionInterval = interval;
    }

    var slidingExpiration = builder.Configuration.GetValue<string>("PostgresCache:DefaultSlidingExpiration");
    if (!string.IsNullOrEmpty(slidingExpiration) && TimeSpan.TryParse(slidingExpiration, out var sliding)) {
        options.DefaultSlidingExpiration = sliding;
    }
});

var app = builder.Build();
  1. Usar o cache
public class MyService {
    private readonly IDistributedCache _cache; 

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

    public async Task<string> GetDataAsync(string key) {
        var cachedData = await _cache.GetStringAsync(key);

        if (cachedData == null) {
            // Fetch data from source
            var data = await FetchDataFromSource();

            // Cache the data with options
            var options = new DistributedCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
                SlidingExpiration = TimeSpan.FromMinutes(5)
            };

            await _cache.SetStringAsync(key, data, options);
            return data;
        }

        return cachedData;
    }
}

Cache distribuído NCache

NCache é um cache distribuído em memória de código aberto desenvolvido nativamente em .NET e .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 sua máquina local, consulte Guia de Introdução para Windows (.NET e .NET Core).

Para configurar o NCache:

  1. Instale NuGet de código aberto NCache.

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

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

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

Usar o cache distribuído

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

Quando o aplicativo de exemplo é iniciado, IDistributedCache é injetado no 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.

Cada vez que a página Índice é carregada, o cache é verificado para o tempo armazenado em cache no OnGetAsync. Se o tempo armazenado em cache não tiver expirado, o tempo será exibido. Se tiverem decorrido 20 segundos desde a última vez que a hora em cache foi acedida (a última vez que esta página foi carregada), a página apresenta Tempo em Cache Expirado.

Atualize imediatamente a hora armazenada em cache para a hora atual selecionando o botão Redefinir hora em cache . O botão aciona o método 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();
    }
}

Note

Não há necessidade de usar um ciclo de vida Singleton ou Scoped para IDistributedCache instâncias (pelo menos para as implementações embutidas).

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

Recommendations

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

  • Infraestrutura existente
  • Requisitos de desempenho
  • Cost
  • Experiência de equipa

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 caro para expandir. Armazene apenas os dados mais usados em um cache.

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

Quando o SQL Server é usado como um armazenamento 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. Recomendamos o uso de uma instância dedicada do SQL Server para o armazenamento de backup de cache distribuído.

Recursos adicionais