Compartir a través de


Biblioteca HybridCache en ASP.NET Core

Importante

HybridCache actualmente todavía está en versión preliminar, pero se publicará completamente después de .NET 9.0 en una futura versión secundaria de extensiones de .NET.

En este artículo se explica cómo configurar y usar la biblioteca HybridCache en una aplicación ASP.NET Core. Para obtener una introducción a la biblioteca, consulte la sección HybridCache de información general sobre el almacenamiento en caché.

Obtención de la biblioteca

Instale el paquete Microsoft.Extensions.Caching.Hybrid.

dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"

Registro del servicio

Agregue el servicio HybridCache al contenedor de inserción de dependencias (DI) mediante una llamada a AddHybridCache:

// Add services to the container.
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthorization();

builder.Services.AddHybridCache();

El código anterior registra el servicio HybridCache con opciones predeterminadas. La API de registro también puede configurar opciones y serialización.

Obtención y almacenamiento de entradas de caché

El servicio HybridCache proporciona un método GetOrCreateAsync con dos sobrecargas, tomando una clave y:

  • Un método de generador.
  • Estado y un método de generador.

El método usa la clave para intentar recuperar el objeto de la caché principal. Si el elemento no se encuentra en la memoria caché principal (un error en la memoria caché), entonces se comprueba la memoria caché secundaria, de haber una configurada. Si no encuentra los datos allí (otro error de caché), llama al método factory para obtener el objeto del origen de datos. A continuación, almacena el objeto en las memorias caché principal y secundaria. Nunca se llama al método factory si el objeto se encuentra en la memoria caché principal o secundaria (se alcanza una caché).

El servicio HybridCache garantiza que solo un llamador simultáneo para una clave determinada llame al método factory y todos los demás autores de llamadas esperan el resultado de esa llamada. El CancellationToken que se pasa a GetOrCreateAsync representa la cancelación combinada de todos los llamadores simultáneos.

La sobrecarga principal GetOrCreateAsync

Se recomienda la sobrecarga sin estado de GetOrCreateAsync para la mayoría de los escenarios. El código que se va a llamar es relativamente sencillo. Este es un ejemplo:

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            cancellationToken: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

La sobrecarga alternativa de GetOrCreateAsync

La sobrecarga alternativa podría reducir cierta sobrecarga de variables capturadas y devoluciones de llamada por instancia, pero a costa del código más complejo. En la mayoría de los escenarios, el aumento del rendimiento no compensa la complejidad del código. Este es un ejemplo que usa la sobrecarga alternativa:

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            (name, id, obj: this),
            static async (state, token) =>
            await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
            cancellationToken: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

El método SetAsync

En muchos escenarios, GetOrCreateAsync es la única API necesaria. Pero HybridCache también tiene SetAsync para almacenar un objeto en caché sin intentar recuperarlo primero.

Eliminación de entradas de caché por clave

Cuando los datos subyacentes de una entrada de caché cambien antes de que expiren, se quita la entrada explícitamente llamando a RemoveAsync con la clave a la entrada. Una sobrecarga permite especificar una colección de valores de clave.

Cuando se quita una entrada, se quita de las memorias caché principal y secundaria.

Eliminación de entradas de caché por etiqueta

Las etiquetas se pueden usar para agrupar entradas de caché e invalidarlas conjuntamente.

Establezca etiquetas al llamar a GetOrCreateAsync, como se muestra en el ejemplo siguiente:

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        var tags = new List<string> { "tag1", "tag2", "tag3" };
        var entryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(1),
            LocalCacheExpiration = TimeSpan.FromMinutes(1)
        };
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            entryOptions,
            tags,
            cancellationToken: token
        );
    }
    
    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Quite todas las entradas de una etiqueta especificada llamando a RemoveByTagAsync con el valor de etiqueta. Una sobrecarga permite especificar una colección de valores de etiqueta.

Cuando se quita una entrada, se quita de las memorias caché principal y secundaria.

Opciones

El método AddHybridCache se puede usar para configurar los valores predeterminados globales. En el ejemplo siguiente se muestra cómo configurar algunas de las opciones disponibles:

// Add services to the container.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
    {
        options.MaximumPayloadBytes = 1024 * 1024;
        options.MaximumKeyLength = 1024;
        options.DefaultEntryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(5),
            LocalCacheExpiration = TimeSpan.FromMinutes(5)
        };
    });

El método GetOrCreateAsync también puede tomar un objeto HybridCacheEntryOptions para invalidar los valores predeterminados globales de una entrada de caché específica. Este es un ejemplo:

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        var tags = new List<string> { "tag1", "tag2", "tag3" };
        var entryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(1),
            LocalCacheExpiration = TimeSpan.FromMinutes(1)
        };
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            entryOptions,
            tags,
            cancellationToken: token
        );
    }
    
    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Para obtener más información sobre las opciones, consulte el código fuente:

Límites

Las siguientes propiedades de HybridCacheOptions permiten configurar límites que se aplican a todas las entradas de caché:

  • MaximumPayloadBytes: tamaño máximo de una entrada de caché. El valor predeterminado es 1 MB. Los intentos de almacenar valores por encima de este tamaño se registran y el valor no se almacena en la memoria caché.
  • MaximumKeyLength: longitud máxima de una clave de caché. El valor predeterminado es de 1024 caracteres. Los intentos de almacenar valores por encima de este tamaño se registran y el valor no se almacena en la memoria caché.

Serialización

Para usar una caché secundaria fuera de proceso se necesita la serialización. La serialización se configura como parte del registro del servicio HybridCache. Se pueden configurar serializadores de uso general y específicos del tipo mediante los métodos AddSerializer y AddSerializerFactory, encadenados desde la llamada a AddHybridCache. De manera predeterminada, el servicio controla internamente string y byte[], y usa System.Text.Json para todo lo demás. HybridCache también puede usar otros serializadores, como protobuf o XML.

En el ejemplo siguiente se configura el servicio para usar un serializador protobuf específico del tipo:

// Add services to the container.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
    {
        options.DefaultEntryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromSeconds(10),
            LocalCacheExpiration = TimeSpan.FromSeconds(5)
        };
    }).AddSerializer<SomeProtobufMessage, 
        GoogleProtobufSerializer<SomeProtobufMessage>>();

En el ejemplo siguiente se configura el servicio para usar un serializador protobuf de uso general que puede controlar muchos tipos de protobuf:

// Add services to the container.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromSeconds(10),
        LocalCacheExpiration = TimeSpan.FromSeconds(5)
    };
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();

La caché secundaria necesita un almacén de datos, como Redis o SqlServer. Para usar Azure Cache for Redis, por ejemplo:

  • Instale el paquete Microsoft.Extensions.Caching.StackExchangeRedis.

  • Cree una instancia de Azure Cache for Redis.

  • Obtenga una cadena de conexión que se conecte a la instancia de Redis. Para buscar la cadena de conexión, seleccione Mostrar claves de acceso en la página Información general de Azure Portal.

  • Almacene la cadena de conexión en la configuración de la aplicación. Por ejemplo, usa un archivo de secretos de usuario similar al siguiente JSON, con la cadena de conexión en la sección ConnectionStrings. Reemplace <the connection string> por la cadena de conexión real:

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • En la inserción de dependencias, registre la implementación de IDistributedCache que proporciona el paquete de Redis. Para ello, llame a AddStackExchangeRedisCache y pase la cadena de conexión. Por ejemplo:

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • La implementación IDistributedCache de Redis ya está disponible en el contenedor de inserción de dependencias de la aplicación. En HybridCache se usa como caché secundaria y se utiliza el serializador que se le ha configurado.

Para más información, vea la aplicación de ejemplo de serialización HybridCache.

Almacenamiento en caché

De forma predeterminada, HybridCache usa MemoryCache para su almacenamiento de caché principal. Las entradas en caché se almacenan en proceso, por lo que cada servidor tiene una caché independiente que se pierde cada vez que se reinicia el proceso del servidor. En el caso del almacenamiento fuera de proceso secundario, como Redis o SQL Server, HybridCache usa la implementación de IDistributedCache configurada, si existe. Pero incluso sin una implementación de IDistributedCache, el servicio HybridCache todavía proporciona almacenamiento en caché en proceso y protección de stampede.

Optimización del rendimiento

Para optimizar el rendimiento, configure HybridCache para reutilizar objetos y evitar asignaciones byte[].

Reutilización de objetos

Al reutilizar instancias, HybridCache puede reducir la sobrecarga de las asignaciones de CPU y objetos asociadas a la deserialización por llamada. Esto puede provocar mejoras de rendimiento en escenarios en los que los objetos almacenados en caché son grandes o a los que se accede con frecuencia.

En el código existente típico que usa IDistributedCache, cada recuperación de un objeto de la memoria caché da como resultado la deserialización. Este comportamiento significa que cada llamador simultáneo obtiene una instancia independiente del objeto, que no puede interactuar con otras instancias. El resultado es la seguridad de subprocesos, ya que no hay ningún riesgo de modificaciones simultáneas en la misma instancia de objeto.

Dado que gran parte del uso de HybridCache se adaptará del código de IDistributedCache existente, HybridCache conserva este comportamiento de forma predeterminada para evitar introducir errores de simultaneidad. Sin embargo, los objetos son intrínsecamente seguros para subprocesos si:

  • Son tipos inmutables.
  • El código no los modifica.

En tales casos, informe a HybridCache de que es seguro reutilizar instancias mediante:

  • Marcación del tipo como sealed. La palabra clave sealed en C# significa que la clase no se puede heredar.
  • Aplicar el atributo [ImmutableObject(true)] al tipo . El atributo [ImmutableObject(true)] indica que el estado del objeto no se puede cambiar después de crearlo.

Evitar asignaciones de byte[]

HybridCache también proporciona API opcionales para implementaciones de IDistributedCache, para evitar byte[] asignaciones. Esta característica se implementa mediante las versiones preliminares de los paquetes de Microsoft.Extensions.Caching.StackExchangeRedis y Microsoft.Extensions.Caching.SqlServer. Para obtener más información, consulte IBufferDistributedCache Estos son los comandos de la CLI de .NET para instalar los paquetes:

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis --prerelease
dotnet add package Microsoft.Extensions.Caching.SqlServer --prerelease

Implementaciones de HybridCache personalizadas

En el marco compartido se incluye una implementación concreta de la clase abstracta HybridCache y se proporciona a través de la inserción de dependencias. Pero los desarrolladores pueden proporcionar implementaciones personalizadas de la API.

Compatibilidad

La biblioteca HybridCache admite entornos de ejecución de .NET anteriores, hasta .NET Framework 4.7.2 y .NET Standard 2.0.

Recursos adicionales

Para obtener más información acerca de HybridCache, vea los siguientes recursos: