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 10 deste artigo.

Uma cache distribuída é uma cache partilhada por múltiplos servidores de aplicações. A cache é normalmente mantida como um serviço externo para os servidores de aplicações que a acedem. Uma cache distribuída pode melhorar o desempenho e a escalabilidade de uma aplicação ASP.NET Core, especialmente quando um serviço cloud ou uma farm de servidores aloja a aplicação.

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 não Microsoft também estão disponíveis, como NCache (NCache em GitHub), Azure Cosmos DB e Postgres. Independentemente da implementação selecionada, a aplicação interage com a cache através da 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:

Usar a 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[] array) à cache usando uma chave de string.
  • 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

Registar uma implementação de IDistributedCache no ficheiro Program.cs . As seguintes implementações fornecidas pelo framework são descritas neste artigo:

Cache Redis distribuída

A cache Redis distribuída oferece o melhor desempenho e é recomendada para aplicações de produção. 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. Para mais informações, consulte recomendações sobre a cache.

Uma aplicação configura a implementação da cache com uma RedisCache instância chamando o AddStackExchangeRedisCache método. Para cache de saída, use o AddStackExchangeRedisOutputCache método.

  1. Crie uma instância do Cache do Azure para Redis.

  2. Copie a cadeia de ligação principal (StackExchange.Redis) para Configuration.

    • Para desenvolvimento local: Guarde a cadeia de ligação com Secret Manager.

    • Para Azure: Guarde o cadeia de ligação numa memória segura como 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 assume que a cadeia de ligação primária (StackExchange.Redis) é guardada nas configurações com a chave MyRedisConStr.

Para mais informações, consulte Azure Managed Redis.

Para uma discussão sobre abordagens alternativas a uma cache Redis local, veja GitHub /dotnet/aspnetcore edição #19542.

Cache de memória distribuída

A cache de memória distribuída (AddDistributedMemoryCache) é uma implementação fornecida pelo framework que IDistributedCache armazena itens na memória. No entanto, a cache de memória distribuída não é uma cache distribuída propriamente dita. A instância da aplicação armazena os itens em cache no servidor onde a aplicação está a correr.

A cache de memória distribuída é uma implementação útil para cenários de desenvolvimento e testes. Também é útil para um único servidor num cenário de produção onde 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.

A aplicação de exemplo utiliza a cache de memória distribuída quando a aplicação corre no Development ambiente do ficheiro Program.cs .

builder.Services.AddDistributedMemoryCache();

Cache SQL Server distribuído

A implementação de cache SQL Server distribuída (AddDistributedSqlServerCache) permite que a cache distribuída utilize uma base de dados SQL Server como seu armazenamento de apoio. 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

Quando a ferramenta tem sucesso, é registada uma mensagem:

Table and index were created successfully.

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

Captura de ecrã que mostra o esquema de uma tabela de cache SQL Server criada com o comando 'sql-cache create'.

Note

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

A aplicação de exemplo implementa a SqlServerCache classe num ambiente não de desenvolvimento (Development) no ficheiro Program.cs :

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

Note

Propriedades como ConnectionString (e opcionalmente, SchemaName e TableName) são normalmente armazenadas fora do controlo de versão. Por exemplo, o Secret Manager ou o appsettings.json ou appsettings.{ Ambiente}.json ficheiro pode armazenar as propriedades. A cadeia de ligação pode conter credenciais que devem ser mantidas fora dos sistemas de controlo de versão.

Para mais informações, consulte SQL Database em Azure.

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. Base de Dados do Azure para PostgreSQL é uma oferta Database-as-a-Service (DBaaS) totalmente gerida e pronta para IA, construída sobre o motor open-source PostgreSQL. O design suporta cargas de trabalho críticas com desempenho previsível, segurança robusta, alta disponibilidade e escalabilidade fluida.

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

  1. Registe o Serviço.

    using Microsoft.Extensions.DependencyInjection;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Register the 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();
    
  2. Usa a 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 the 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 NCache distribuída

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 na sua máquina local, consulte o Guia de Iniciação.

Para configurar o NCache:

  1. Instale o pacote NuGet NCache SDK, que suporta o NCache Opensource para .NET Framework e .NET aplicações Core.

  2. Configure o cluster de cache na configuração do cliente (o ficheiro client.ncconf ).

  3. Adicione o seguinte código ao ficheiro Program.cs :

builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Cache Distribuído do Azure Cosmos DB

Azure Cosmos DB pode ser configurado em ASP.NET Core como fornecedor de estado de sessão usando a 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 de missão crítica.

Depois de instalar o Microsoft. Extensions.Caching.Cosmos pacote NuGet, configure uma cache distribuída Azure Cosmos DB. Pode usar um cliente Azure Cosmos DB existente ou criar um novo, conforme descrito nas secções seguintes.

Para mais informações, consulte a Extensão de Cache Microsoft usando Azure Cosmos DB, o ficheiro README do repositório GitHub para o pacote NuGet.

Reutilizar um cliente existente

A forma mais fácil de configurar uma cache distribuída é reutilizando um cliente Azure Cosmos DB existente. Neste caso, a instância CosmosClient não é eliminada quando o fornecedor é dispensado.

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. Neste caso, a CosmosClient instância é descartada quando o fornecedor é 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 a aplicação de exemplo inicia, a IDistributedCache instância é injetada no ficheiro Program.cs . A hora atual é armazenada em cache através da IHostApplicationLifetime interface. (Para mais informações, veja .NET 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);
});

A aplicação de exemplo injeta a IDistributedCache instância no IndexModel objeto para ser usada pela página de Índice.

Cada vez que a página do Índice é carregada, a cache é verificada quanto ao tempo armazenado em cache usando o método OnGetAsync. Se o tempo em cache não tiver expirado, o tempo é exibido. Se passaram 20 segundos desde a última vez que o tempo em cache foi acedido (a última vez que esta página carregou), a página mostra a mensagem, Tempo em Cache Expirado.

Atualize imediatamente o tempo em cache para o tempo atual selecionando a opção Reiniciar o Tempo em Cache . Esta ação ativa o OnPostResetCachedTime método do manipulador.

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();
    }
}

Note

Não precisas de usar um Singleton ou Scoped lifetime para IDistributedCache instâncias com implementação incorporada.

Também podes criar uma IDistributedCache instância onde precisares de uma em vez de usar DI. No entanto, criar uma instância em código pode tornar o seu código mais difícil de testar e viola o Princípio das Dependências Explícitas.

Consulte as recomendações de cache

Ao decidir qual a melhor implementação da IDistributedCache interface para a sua aplicação, considere os seguintes pontos:

  • 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.

Se o SQL Server for o armazenamento distribuído de suporte da cache, e o armazenamento/recuperação de dados da cache e da aplicação usarem a mesma base de dados, o desempenho pode ser reduzido. A abordagem recomendada é usar uma instância dedicada do SQL Server para o armazenamento de cache distribuído.

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 Cache do Azure para 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.

A aplicação de exemplo utiliza a Cache de Memória Distribuída quando a aplicação é executada no Development ambiente 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.

A aplicação de exemplo implementa SqlServerCache num ambiente não-Development 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 a memória 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.

A aplicação de exemplo utiliza a Cache de Memória Distribuída quando a aplicação é executada no Development ambiente 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.

A aplicação de exemplo implementa SqlServerCache num ambiente não-Development 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 Cache do Azure para 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 a memória 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