Memorizzazione nella cache distribuita in ASP.NET Core

Di Mohsin Nasir e smandia

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Una cache distribuita è una cache condivisa da più server app, in genere gestita come servizio esterno ai server app che vi accedono. Una cache distribuita può migliorare le prestazioni e la scalabilità di un'app ASP.NET Core, soprattutto quando l'app è ospitata da un servizio cloud o da una server farm.

Una cache distribuita offre diversi vantaggi rispetto ad altri scenari di memorizzazione nella cache in cui i dati memorizzati nella cache vengono archiviati in singoli server app.

Quando i dati memorizzati nella cache vengono distribuiti, i dati:

  • Coerente (coerente ) tra le richieste a più server.
  • Sopravvive ai riavvii del server e alle distribuzioni di app.
  • Non usa memoria locale.

La configurazione della cache distribuita è specifica dell'implementazione. Questo articolo descrive come configurare le cache distribuite di SQL Server e Redis. Sono disponibili anche implementazioni di terze parti, ad esempio NCache (NCache in GitHub). Indipendentemente dall'implementazione selezionata, l'app interagisce con la cache usando l'interfaccia IDistributedCache .

Visualizzare o scaricare il codice di esempio (procedura per il download)

Prerequisiti

Aggiungere un riferimento al pacchetto per il provider di cache distribuita usato:

Interfaccia IDistributedCache

L'interfaccia IDistributedCache fornisce i metodi seguenti per modificare gli elementi nell'implementazione della cache distribuita:

  • Get, GetAsync: accetta una chiave stringa e recupera un elemento memorizzato nella cache come byte[] matrice, se presente nella cache.
  • Set, SetAsync: aggiunge un elemento (come byte[] matrice) alla cache usando una chiave stringa.
  • Refresh, RefreshAsync: aggiorna un elemento nella cache in base alla relativa chiave, reimpostando il timeout di scadenza scorrevole (se presente).
  • Remove, RemoveAsync: rimuove un elemento della cache in base alla relativa chiave stringa.

Stabilire servizi di memorizzazione nella cache distribuita

Registrare un'implementazione di IDistributedCache in Program.cs. Le implementazioni fornite dal framework descritte in questo argomento includono:

Cache Redis distribuita

È consigliabile che le app di produzione usino la cache Redis distribuita perché è la più efficiente. Per altre informazioni, vedere Consigli.

Redis è un archivio dati open source in memoria, che viene spesso usato come cache distribuita. È possibile configurare un cache di Azure per Redis per un'app ASP.NET Core ospitata in Azure e usare un cache di Azure per Redis per lo sviluppo locale.

Un'app configura l'implementazione della cache usando un'istanza RedisCache di chiamando AddStackExchangeRedisCache. Per la memorizzazione nella cache dell'output, usare AddStackExchangeRedisOutputCache.

  1. Creare un cache di Azure per Redis.
  2. Copiare il stringa di connessione primario (StackExchange.Redis) in Configurazione.
    • Sviluppo locale: salvare il stringa di connessione con Secret Manager.
    • Azure: salvare il stringa di connessione nella configurazione servizio app o in un altro archivio protetto.

Il codice seguente abilita il cache di Azure per Redis:

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

Il codice precedente presuppone che il stringa di connessione primario (StackExchange.Redis) sia stato salvato nella configurazione con il nome MyRedisConStrdella chiave .

Per altre informazioni, vedere Azure Cache for Redis.

Vedere questo problema di GitHub per una discussione sugli approcci alternativi a una cache Redis locale.

Cache di memoria distribuita

La cache di memoria distribuita (AddDistributedMemoryCache) è un'implementazione fornita dal framework di IDistributedCache che archivia gli elementi in memoria. La cache di memoria distribuita non è una cache distribuita effettiva. Gli elementi memorizzati nella cache vengono archiviati dall'istanza dell'app nel server in cui è in esecuzione l'app.

La cache di memoria distribuita è un'implementazione utile:

  • Negli scenari di sviluppo e test.
  • Quando un singolo server viene usato nell'ambiente di produzione e nel consumo di memoria non è un problema. L'implementazione della cache di memoria distribuita astrae l'archiviazione dei dati memorizzata nella cache. Consente di implementare una vera soluzione di memorizzazione nella cache distribuita in futuro se sono necessari più nodi o tolleranza di errore.

L'app di esempio usa cache di memoria distribuita quando l'app viene eseguita nell'ambiente di sviluppo in Program.cs:

builder.Services.AddDistributedMemoryCache();

Cache di SQL Server distribuita

L'implementazione della cache di SQL Server distribuita (AddDistributedSqlServerCache) consente alla cache distribuita di usare un database di SQL Server come archivio di backup. Per creare una tabella di elementi memorizzata nella cache di SQL Server in un'istanza di SQL Server, è possibile usare lo sql-cache strumento . Lo strumento crea una tabella con il nome e lo schema specificati.

Creare una tabella in SQL Server eseguendo il sql-cache create comando . Specificare l'istanza di SQL Server (Data Source), il database (Initial Catalog), lo schema (ad esempio, dbo) e il nome della tabella (ad esempio, TestCache):

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

Viene registrato un messaggio per indicare che lo strumento ha avuto esito positivo:

Table and index were created successfully.

La tabella creata dallo sql-cache strumento ha lo schema seguente:

Tabella della cache di SqlServer

Nota

Un'app deve modificare i valori della cache usando un'istanza di IDistributedCache, non un oggetto SqlServerCache.

L'app di esempio implementa SqlServerCache in un ambiente non di sviluppo in Program.cs:

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

Nota

Un ConnectionString oggetto (e facoltativamente SchemaName e TableName) viene in genere archiviato all'esterno del controllo del codice sorgente (ad esempio, archiviato da Secret Manager o nei appsettings.json/appsettings.{Environment}.json file). Il stringa di connessione può contenere credenziali che devono essere mantenute fuori dai sistemi di controllo del codice sorgente.

Cache NCache distribuita

NCache è una cache distribuita in memoria open source sviluppata in modo nativo in .NET e .NET Core. NCache funziona sia in locale che configurato come cluster di cache distribuita per un'app ASP.NET Core in esecuzione in Azure o in altre piattaforme di hosting.

Per installare e configurare NCache nel computer locale, vedere Guida introduttiva per Windows (.NET e .NET Core).

Per configurare NCache:

  1. Installare NuGet open source NCache.
  2. Configurare il cluster della cache in client.ncconf.
  3. Aggiungere il codice seguente a Program.cs:
builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Cache di Azure CosmosDB distribuita

Azure Cosmos DB può essere usato in ASP.NET Core come provider di stato della sessione usando l'interfaccia IDistributedCache . Azure Cosmos DB è un database noSQL e relazionale completamente gestito per lo sviluppo di app moderne che offre disponibilità elevata, scalabilità e accesso a bassa latenza ai dati per applicazioni cruciali.

Dopo aver installato il pacchetto NuGet Microsoft.Extensions.Caching.Cosmos , configurare una cache distribuita di Azure Cosmos DB come indicato di seguito:

Riutilizzare un client esistente

Il modo più semplice per configurare la cache distribuita consiste nel riutilizzare un client Azure Cosmos DB esistente. In questo caso, l'istanza CosmosClient non verrà eliminata quando il provider viene eliminato.

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

Creare un nuovo client

In alternativa, creare un'istanza di un nuovo client. In questo caso, l'istanza CosmosClient verrà eliminata quando il provider viene eliminato.

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

Usare la cache distribuita

Per usare l'interfaccia IDistributedCache , richiedere un'istanza di IDistributedCache nell'app. L'istanza viene fornita dall'inserimento delle dipendenze (DI).The instance is provided by dependency injection (DI).

All'avvio dell'app di esempio, IDistributedCache viene inserito in Program.cs. L'ora corrente viene memorizzata nella cache usando IHostApplicationLifetime (per altre informazioni, vedere Host generico: 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);
});

L'app di esempio inserisce IDistributedCache nell'oggetto IndexModel per l'uso dalla pagina Indice.

Ogni volta che viene caricata la pagina Indice, la cache viene verificata per il tempo memorizzato nella cache in OnGetAsync. Se l'ora memorizzata nella cache non è scaduta, viene visualizzata l'ora. Se sono trascorsi 20 secondi dall'ultima volta che è stato eseguito l'accesso al tempo memorizzato nella cache (l'ultima volta che questa pagina è stata caricata), nella pagina viene visualizzato l'ora memorizzata nella cache scaduta.

Aggiornare immediatamente l'ora memorizzata nella cache all'ora corrente selezionando il pulsante Reimposta ora memorizzata nella cache. Il pulsante attiva il OnPostResetCachedTime metodo del gestore.

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

Non è necessario usare una durata singleton o con ambito per IDistributedCache le istanze con le implementazioni predefinite.

È anche possibile creare un'istanza IDistributedCache ovunque sia necessario anziché usare l'inserimento delle dipendenze, ma la creazione di un'istanza nel codice può rendere il codice più difficile da testare e violare il principio delle dipendenze esplicite.

Consigli

Quando decidi quale implementazione di IDistributedCache è migliore per la tua app, prendi in considerazione quanto segue:

  • Infrastruttura esistente
  • Requisiti per le prestazioni
  • Costo
  • Esperienza del team

Le soluzioni di memorizzazione nella cache si basano in genere sull'archiviazione in memoria per fornire un recupero rapido dei dati memorizzati nella cache, ma la memoria è una risorsa limitata e costosa da espandere. Archiviare solo i dati di uso comune in una cache.

Per la maggior parte delle app, una cache Redis offre una velocità effettiva più elevata e una latenza inferiore rispetto a una cache di SQL Server. Tuttavia, è consigliabile eseguire il benchmarking per determinare le caratteristiche delle prestazioni delle strategie di memorizzazione nella cache.

Quando SQL Server viene usato come archivio di backup della cache distribuita, l'uso dello stesso database per la cache e l'archiviazione e il recupero dei dati ordinari dell'app possono influire negativamente sulle prestazioni di entrambi. È consigliabile usare un'istanza di SQL Server dedicata per l'archivio di backup della cache distribuita.

Risorse aggiuntive

Una cache distribuita è una cache condivisa da più server app, in genere gestita come servizio esterno ai server app che vi accedono. Una cache distribuita può migliorare le prestazioni e la scalabilità di un'app ASP.NET Core, soprattutto quando l'app è ospitata da un servizio cloud o da una server farm.

Una cache distribuita offre diversi vantaggi rispetto ad altri scenari di memorizzazione nella cache in cui i dati memorizzati nella cache vengono archiviati in singoli server app.

Quando i dati memorizzati nella cache vengono distribuiti, i dati:

  • Coerente (coerente ) tra le richieste a più server.
  • Sopravvive ai riavvii del server e alle distribuzioni di app.
  • Non usa memoria locale.

La configurazione della cache distribuita è specifica dell'implementazione. Questo articolo descrive come configurare le cache distribuite di SQL Server e Redis. Sono disponibili anche implementazioni di terze parti, ad esempio NCache (NCache in GitHub). Indipendentemente dall'implementazione selezionata, l'app interagisce con la cache usando l'interfaccia IDistributedCache .

Visualizzare o scaricare il codice di esempio (procedura per il download)

Prerequisiti

Aggiungere un riferimento al pacchetto per il provider di cache distribuita usato:

Interfaccia IDistributedCache

L'interfaccia IDistributedCache fornisce i metodi seguenti per modificare gli elementi nell'implementazione della cache distribuita:

  • Get, GetAsync: accetta una chiave stringa e recupera un elemento memorizzato nella cache come byte[] matrice, se presente nella cache.
  • Set, SetAsync: aggiunge un elemento (come byte[] matrice) alla cache usando una chiave stringa.
  • Refresh, RefreshAsync: aggiorna un elemento nella cache in base alla relativa chiave, reimpostando il timeout di scadenza scorrevole (se presente).
  • Remove, RemoveAsync: rimuove un elemento della cache in base alla relativa chiave stringa.

Stabilire servizi di memorizzazione nella cache distribuita

Registrare un'implementazione di IDistributedCache in Program.cs. Le implementazioni fornite dal framework descritte in questo argomento includono:

Cache Redis distribuita

È consigliabile che le app di produzione usino la cache Redis distribuita perché è la più efficiente. Per altre informazioni, vedere Consigli.

Redis è un archivio dati open source in memoria, che viene spesso usato come cache distribuita. È possibile configurare una Cache Redis di Azure per un'app ASP.NET Core ospitata in Azure e usare cache Redis di Azure per lo sviluppo locale.

Un'app configura l'implementazione della cache usando un'istanza RedisCache (AddStackExchangeRedisCache).

  1. Creare un cache di Azure per Redis.
  2. Copiare il stringa di connessione primario (StackExchange.Redis) in Configurazione.
    • Sviluppo locale: salvare il stringa di connessione con Secret Manager.
    • Azure: salvare il stringa di connessione nella configurazione servizio app o in un altro archivio protetto.

Il codice seguente abilita il cache di Azure per Redis:

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

Il codice precedente presuppone che il stringa di connessione primario (StackExchange.Redis) sia stato salvato nella configurazione con il nome MyRedisConStrdella chiave .

Per altre informazioni, vedere Azure Cache for Redis.

Vedere questo problema di GitHub per una discussione sugli approcci alternativi a una cache Redis locale.

Cache di memoria distribuita

La cache di memoria distribuita (AddDistributedMemoryCache) è un'implementazione fornita dal framework di IDistributedCache che archivia gli elementi in memoria. La cache di memoria distribuita non è una cache distribuita effettiva. Gli elementi memorizzati nella cache vengono archiviati dall'istanza dell'app nel server in cui è in esecuzione l'app.

La cache di memoria distribuita è un'implementazione utile:

  • Negli scenari di sviluppo e test.
  • Quando un singolo server viene usato nell'ambiente di produzione e nel consumo di memoria non è un problema. L'implementazione della cache di memoria distribuita astrae l'archiviazione dei dati memorizzata nella cache. Consente di implementare una vera soluzione di memorizzazione nella cache distribuita in futuro se sono necessari più nodi o tolleranza di errore.

L'app di esempio usa cache di memoria distribuita quando l'app viene eseguita nell'ambiente di sviluppo in Program.cs:

builder.Services.AddDistributedMemoryCache();

Cache di SQL Server distribuita

L'implementazione della cache di SQL Server distribuita (AddDistributedSqlServerCache) consente alla cache distribuita di usare un database di SQL Server come archivio di backup. Per creare una tabella di elementi memorizzata nella cache di SQL Server in un'istanza di SQL Server, è possibile usare lo sql-cache strumento . Lo strumento crea una tabella con il nome e lo schema specificati.

Creare una tabella in SQL Server eseguendo il sql-cache create comando . Specificare l'istanza di SQL Server (Data Source), il database (Initial Catalog), lo schema (ad esempio, dbo) e il nome della tabella (ad esempio, TestCache):

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

Viene registrato un messaggio per indicare che lo strumento ha avuto esito positivo:

Table and index were created successfully.

La tabella creata dallo sql-cache strumento ha lo schema seguente:

Tabella della cache di SqlServer

Nota

Un'app deve modificare i valori della cache usando un'istanza di IDistributedCache, non un oggetto SqlServerCache.

L'app di esempio implementa SqlServerCache in un ambiente non di sviluppo in Program.cs:

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

Nota

Un ConnectionString oggetto (e facoltativamente SchemaName e TableName) viene in genere archiviato all'esterno del controllo del codice sorgente (ad esempio, archiviato da Secret Manager o nei appsettings.json/appsettings.{Environment}.json file). Il stringa di connessione può contenere credenziali che devono essere mantenute fuori dai sistemi di controllo del codice sorgente.

Cache NCache distribuita

NCache è una cache distribuita in memoria open source sviluppata in modo nativo in .NET e .NET Core. NCache funziona sia in locale che configurato come cluster di cache distribuita per un'app ASP.NET Core in esecuzione in Azure o in altre piattaforme di hosting.

Per installare e configurare NCache nel computer locale, vedere Guida introduttiva per Windows (.NET e .NET Core).

Per configurare NCache:

  1. Installare NuGet open source NCache.
  2. Configurare il cluster della cache in client.ncconf.
  3. Aggiungere il codice seguente a Program.cs:
builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Usare la cache distribuita

Per usare l'interfaccia IDistributedCache , richiedere un'istanza di IDistributedCache nell'app. L'istanza viene fornita dall'inserimento delle dipendenze (DI).The instance is provided by dependency injection (DI).

All'avvio dell'app di esempio, IDistributedCache viene inserito in Program.cs. L'ora corrente viene memorizzata nella cache usando IHostApplicationLifetime (per altre informazioni, vedere Host generico: 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);
});

L'app di esempio inserisce IDistributedCache nell'oggetto IndexModel per l'uso dalla pagina Indice.

Ogni volta che viene caricata la pagina Indice, la cache viene verificata per il tempo memorizzato nella cache in OnGetAsync. Se l'ora memorizzata nella cache non è scaduta, viene visualizzata l'ora. Se sono trascorsi 20 secondi dall'ultima volta che è stato eseguito l'accesso al tempo memorizzato nella cache (l'ultima volta che questa pagina è stata caricata), nella pagina viene visualizzato l'ora memorizzata nella cache scaduta.

Aggiornare immediatamente l'ora memorizzata nella cache all'ora corrente selezionando il pulsante Reimposta ora memorizzata nella cache. Il pulsante attiva il OnPostResetCachedTime metodo del gestore.

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

Non è necessario usare una durata singleton o con ambito per IDistributedCache le istanze con le implementazioni predefinite.

È anche possibile creare un'istanza IDistributedCache ovunque sia necessario anziché usare l'inserimento delle dipendenze, ma la creazione di un'istanza nel codice può rendere il codice più difficile da testare e violare il principio delle dipendenze esplicite.

Consigli

Quando decidi quale implementazione di IDistributedCache è migliore per la tua app, prendi in considerazione quanto segue:

  • Infrastruttura esistente
  • Requisiti per le prestazioni
  • Costo
  • Esperienza del team

Le soluzioni di memorizzazione nella cache si basano in genere sull'archiviazione in memoria per fornire un recupero rapido dei dati memorizzati nella cache, ma la memoria è una risorsa limitata e costosa da espandere. Archiviare solo i dati di uso comune in una cache.

Per la maggior parte delle app, una cache Redis offre una velocità effettiva più elevata e una latenza inferiore rispetto a una cache di SQL Server. Tuttavia, è consigliabile eseguire il benchmarking per determinare le caratteristiche delle prestazioni delle strategie di memorizzazione nella cache.

Quando SQL Server viene usato come archivio di backup della cache distribuita, l'uso dello stesso database per la cache e l'archiviazione e il recupero dei dati ordinari dell'app possono influire negativamente sulle prestazioni di entrambi. È consigliabile usare un'istanza di SQL Server dedicata per l'archivio di backup della cache distribuita.

Risorse aggiuntive

Una cache distribuita è una cache condivisa da più server app, in genere gestita come servizio esterno ai server app che vi accedono. Una cache distribuita può migliorare le prestazioni e la scalabilità di un'app ASP.NET Core, soprattutto quando l'app è ospitata da un servizio cloud o da una server farm.

Una cache distribuita offre diversi vantaggi rispetto ad altri scenari di memorizzazione nella cache in cui i dati memorizzati nella cache vengono archiviati in singoli server app.

Quando i dati memorizzati nella cache vengono distribuiti, i dati:

  • Coerente (coerente ) tra le richieste a più server.
  • Sopravvive ai riavvii del server e alle distribuzioni di app.
  • Non usa memoria locale.

La configurazione della cache distribuita è specifica dell'implementazione. Questo articolo descrive come configurare le cache distribuite di SQL Server e Redis. Sono disponibili anche implementazioni di terze parti, ad esempio NCache (NCache in GitHub). Indipendentemente dall'implementazione selezionata, l'app interagisce con la cache usando l'interfaccia IDistributedCache .

Visualizzare o scaricare il codice di esempio (procedura per il download)

Prerequisiti

Per usare una cache distribuita di SQL Server, aggiungere un riferimento al pacchetto Microsoft.Extensions.Caching.SqlServer .

Per usare una cache distribuita Redis, aggiungere un riferimento al pacchetto Microsoft.Extensions.Caching.StackExchangeRedis .

Per usare la cache distribuita NCache, aggiungere un riferimento al pacchetto NCache.Microsoft.Extensions.Caching.OpenSource .

Interfaccia IDistributedCache

L'interfaccia IDistributedCache fornisce i metodi seguenti per modificare gli elementi nell'implementazione della cache distribuita:

  • Get, GetAsync: accetta una chiave stringa e recupera un elemento memorizzato nella cache come byte[] matrice, se presente nella cache.
  • Set, SetAsync: aggiunge un elemento (come byte[] matrice) alla cache usando una chiave stringa.
  • Refresh, RefreshAsync: aggiorna un elemento nella cache in base alla relativa chiave, reimpostando il timeout di scadenza scorrevole (se presente).
  • Remove, RemoveAsync: rimuove un elemento della cache in base alla relativa chiave stringa.

Stabilire servizi di memorizzazione nella cache distribuita

Registrare un'implementazione di IDistributedCache in Startup.ConfigureServices. Le implementazioni fornite dal framework descritte in questo argomento includono:

Cache di memoria distribuita

La cache di memoria distribuita (AddDistributedMemoryCache) è un'implementazione fornita dal framework di IDistributedCache che archivia gli elementi in memoria. La cache di memoria distribuita non è una cache distribuita effettiva. Gli elementi memorizzati nella cache vengono archiviati dall'istanza dell'app nel server in cui è in esecuzione l'app.

La cache di memoria distribuita è un'implementazione utile:

  • Negli scenari di sviluppo e test.
  • Quando un singolo server viene usato nell'ambiente di produzione e nel consumo di memoria non è un problema. L'implementazione della cache di memoria distribuita astrae l'archiviazione dei dati memorizzata nella cache. Consente di implementare una vera soluzione di memorizzazione nella cache distribuita in futuro se sono necessari più nodi o tolleranza di errore.

L'app di esempio usa cache di memoria distribuita quando l'app viene eseguita nell'ambiente di sviluppo in Startup.ConfigureServices:

services.AddDistributedMemoryCache();

Cache di SQL Server distribuita

L'implementazione della cache di SQL Server distribuita (AddDistributedSqlServerCache) consente alla cache distribuita di usare un database di SQL Server come archivio di backup. Per creare una tabella di elementi memorizzata nella cache di SQL Server in un'istanza di SQL Server, è possibile usare lo sql-cache strumento . Lo strumento crea una tabella con il nome e lo schema specificati.

Creare una tabella in SQL Server eseguendo il sql-cache create comando . Specificare l'istanza di SQL Server (Data Source), il database (Initial Catalog), lo schema (ad esempio, dbo) e il nome della tabella (ad esempio, TestCache):

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

Viene registrato un messaggio per indicare che lo strumento ha avuto esito positivo:

Table and index were created successfully.

La tabella creata dallo sql-cache strumento ha lo schema seguente:

Tabella della cache di SqlServer

Nota

Un'app deve modificare i valori della cache usando un'istanza di IDistributedCache, non un oggetto SqlServerCache.

L'app di esempio implementa SqlServerCache in un ambiente non di sviluppo in Startup.ConfigureServices:

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

Nota

Un ConnectionString oggetto (e facoltativamente SchemaName e TableName) viene in genere archiviato all'esterno del controllo del codice sorgente (ad esempio, archiviato da Secret Manager o nei appsettings.json/appsettings.{Environment}.json file). Il stringa di connessione può contenere credenziali che devono essere mantenute fuori dai sistemi di controllo del codice sorgente.

Cache Redis distribuita

Redis è un archivio dati open source in memoria, che viene spesso usato come cache distribuita. È possibile configurare una Cache Redis di Azure per un'app ASP.NET Core ospitata in Azure e usare cache Redis di Azure per lo sviluppo locale.

Un'app configura l'implementazione della cache usando un'istanza RedisCache (AddStackExchangeRedisCache).

  1. Creare un cache di Azure per Redis.
  2. Copiare il stringa di connessione primario (StackExchange.Redis) in Configurazione.
    • Sviluppo locale: salvare il stringa di connessione con Secret Manager.
    • Azure: salvare il stringa di connessione nella configurazione servizio app o in un altro archivio protetto.

Il codice seguente abilita il cache di Azure per Redis:

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

    services.AddRazorPages();
}

Il codice precedente presuppone che il stringa di connessione primario (StackExchange.Redis) sia stato salvato nella configurazione con il nome MyRedisConStrdella chiave .

Per altre informazioni, vedere Azure Cache for Redis.

Vedere questo problema di GitHub per una discussione sugli approcci alternativi a una cache Redis locale.

Cache NCache distribuita

NCache è una cache distribuita in memoria open source sviluppata in modo nativo in .NET e .NET Core. NCache funziona sia in locale che configurato come cluster di cache distribuita per un'app ASP.NET Core in esecuzione in Azure o in altre piattaforme di hosting.

Per installare e configurare NCache nel computer locale, vedere Guida introduttiva per Windows (.NET e .NET Core).

Per configurare NCache:

  1. Installare NuGet open source NCache.

  2. Configurare il cluster della cache in client.ncconf.

  3. Aggiungere il codice seguente a Startup.ConfigureServices:

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

Usare la cache distribuita

Per usare l'interfaccia, richiedere un'istanza IDistributedCache di IDistributedCache da qualsiasi costruttore nell'app. L'istanza viene fornita dall'inserimento delle dipendenze (DI).The instance is provided by dependency injection (DI).

All'avvio dell'app di esempio, IDistributedCache viene inserito in Startup.Configure. L'ora corrente viene memorizzata nella cache usando IHostApplicationLifetime (per altre informazioni, vedere Host generico: 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);
    });

L'app di esempio inserisce IDistributedCache nell'oggetto IndexModel per l'uso dalla pagina Indice.

Ogni volta che viene caricata la pagina Indice, la cache viene verificata per il tempo memorizzato nella cache in OnGetAsync. Se l'ora memorizzata nella cache non è scaduta, viene visualizzata l'ora. Se sono trascorsi 20 secondi dall'ultima volta che è stato eseguito l'accesso al tempo memorizzato nella cache (l'ultima volta che questa pagina è stata caricata), nella pagina viene visualizzato l'ora memorizzata nella cache scaduta.

Aggiornare immediatamente l'ora memorizzata nella cache all'ora corrente selezionando il pulsante Reimposta ora memorizzata nella cache. Il pulsante attiva il OnPostResetCachedTime metodo del gestore.

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

Nota

Non è necessario usare una durata singleton o con ambito per IDistributedCache le istanze (almeno per le implementazioni predefinite).

È anche possibile creare un'istanza IDistributedCache ovunque sia necessario anziché usare l'inserimento delle dipendenze, ma la creazione di un'istanza nel codice può rendere il codice più difficile da testare e violare il principio delle dipendenze esplicite.

Consigli

Quando decidi quale implementazione di IDistributedCache è migliore per la tua app, prendi in considerazione quanto segue:

  • Infrastruttura esistente
  • Requisiti per le prestazioni
  • Costo
  • Esperienza del team

Le soluzioni di memorizzazione nella cache si basano in genere sull'archiviazione in memoria per fornire un recupero rapido dei dati memorizzati nella cache, ma la memoria è una risorsa limitata e costosa da espandere. Archiviare solo i dati di uso comune in una cache.

In genere, una cache Redis offre una velocità effettiva più elevata e una latenza inferiore rispetto a una cache di SQL Server. Tuttavia, il benchmarking è in genere necessario per determinare le caratteristiche delle prestazioni delle strategie di memorizzazione nella cache.

Quando SQL Server viene usato come archivio di backup della cache distribuita, l'uso dello stesso database per la cache e l'archiviazione e il recupero dei dati ordinari dell'app possono influire negativamente sulle prestazioni di entrambi. È consigliabile usare un'istanza di SQL Server dedicata per l'archivio di backup della cache distribuita.

Risorse aggiuntive