Saltar a contenido

Redis

La librería Andreani.ARQ.Redis fue desarrollada para facilitar la integración de los proyecto de .NET con la base de datos Redis

Prerrequisitos

  1. Instalar Net 6
  2. Instalar Visual Studio 2022
  3. Configurar github packages
  4. Instalar o levantar en docker una instancia de Redis.

Como levantar redis en docker

Para levantar redis en docker debemos correr el siguiente comando

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

Nos levantara un container con Redis al cual podemos conectarnos desde la app mediante

Host: localhost
Port: 6379

Además nos provee de una web, entrando en http://localhost:8001/ que nos permite ver la información almacenada en la base de Redis.

redis web

Configuración

Environment

Para configurar la librería debemos agregar la configuración de conexión y demás parámetros en las variables de entorno.

Ejemplo

    Redis:
        Host: localhost
        Port: 6379
        InstanceName: test

Opciones de configuración

name Descripción Default
InstanceName Nombre de la instancia o aplicación, se utiliza para agregar un prefix a las keys en redis, esto evita tener colisiones con keys de otras apps.
Host Dirección de host del server de redis localhost
Port puerto del server 6379
User Usuario de acceso al server
Password Password de acceso al server
Database Selecciona la base de datos de redis que va a trabajar la intancias 0
BatchSize Tamaño de Batch para realizar insert, update o remove masivos 1000
AbsoluteExpirationRelativeToNowSeg Establece el tiempo de vencimiento de una key en la caché, se establece a partir del momento actual en adelante. El valor ingresado se toma como segundos
SlidingExpirationSeg Obtiene o establece cuánto tiempo una key de caché puede estar inactiva, es decir, sin uso, antes de que se elimine. Esto no extenderá el tiempo de vida de la entrada más allá del vencimiento absoluto (si se establece). El valor ingresado se toma como segundos

Tip

Si quieren saber más sobre las diferencias entre AbsoluteExpirationRelativeToNow y SlidingExpirationSeg pueden ver este post

Injection Dependency

En la configuración del services, debemos agregar la siguiente config:

    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
        //...

        services.AddRedis(configuration);

        return services;
    }

Cómo utilizar la librería

Para poder acceder a la implementación debemos inyectar en nuestro código la interface IMemoryDatabase

Ejemplo

    private readonly IMemoryDatabase _cache;

    public ExampleHandler(IMemoryDatabase cache)
    {
        _cache = cache;
    }

Guardar un elemento en Redis

Para almacenar la interface dispone de multiples metodos de set, internamente cada elemento se almacenará como un HSET.

Info

Para saber más que es un HSET ver la docu de redis

Métodos de guardado disponibles

void SetString(string key, string value, DistributedCacheEntryOptions options = null);

Task SetStringAsync(string key, string value, DistributedCacheEntryOptions options = null, CancellationToken token = default(CancellationToken));

void Set<T>(string key, T value, DistributedCacheEntryOptions options = null) where T : class;

void Set<T>(Func<T, string> key, IEnumerable<T> value, DistributedCacheEntryOptions options = null) where T : class;

Task SetAsync<T>(Func<T, string> key, IEnumerable<T> value, DistributedCacheEntryOptions options = null) where T : class;

Task SetAsync<T>(string key, T value, DistributedCacheEntryOptions options = null) where T : class;

Task SetAsync(string key, byte[] value, CancellationToken token = default(CancellationToken));

Ejemplo simple

Almacenamiento de un objeto en redis

public async Task ExampleSet()
{
    var toSave = new AnyObject
    {
        Id=1,
        Name="test"
    };
    var key = "AlgunaKey";
    await _cache.SetAsync(key, toSave);
}

Ejemplo Set Múltiples Objetos

Cuando necesitamos volcar muchos datos a redis podemos utilizar el método que provee una estrategia de publicación en batch, para eso podemos enviarle una lista de elementos

public async Task ExampleBulkSet()
{
    var toSave = new List<AnyObject>(); // muchos elementos
    await _cache.SetAsync((e) => $"AlgunaKey:{e.Id}", toSave);
}

Atención

Esta función (e) => $"AlgunaKey:{e.Id} significa que por cada elemento de nuestra array vamos generar una key con el id del elemento.

Expiración de key

Si hemos configurado una espiración global esta se aplicará en todos los elementos que guardamos, pero podemos configurar para que ignorar la configuración global pasandole una configuración particular:

public async Task ExampleSetWithExp()
{
    var toSave = new AnyObject
    {
        Id=1,
        Name="test"
    };
    var options = new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1),
        SlidingExpiration = TimeSpan.FromHours(1),
    };
    var key = "AlgunaKey";
    await _cache.SetAsync(key, toSave, options);
}

Buscar elementos en Redis

Para buscar la interface dispone de multiples métodos de get.

string GetString(string key);

Task<string> GetStringAsync(string key);

Task<T> GetAsync<T>(string key) where T : class;

T Get<T>(string key) where T : class;

IEnumerable<T> GetAll<T>(string pattern = "*") where T : class;

Task<IEnumerable<T>> GetAllAsync<T>(string pattern = "*") where T : class;

IEnumerable<string> GetKeys(string pattern = "*");

Ejemplo Simple

Obtener un objeto de redis

public async Task ExampleGet()
{
    var key = "AlgunaKey";
    var value = await _cache.GetAsync<AnyObject>(key);

    //...
}

Tip

Este método recibe el objeto que va a mapear, claramente debe contener la misma estructura que el que se encuentra almacenado.

Obtener todos los elementos de un patrón

En caso que necesitemos obtener todos los elementos dado un patrón (regex) de key, por ejemplo, todos los elementos de las key que cumplen con el patron "AlgunaKey" utilizaremos el método GetAll

public async Task ExampleGetAll()
{
    var pattern = "AlgunaKey:*";
    var values = await _cache.GetAllAsync<AnyObject>(pattern);

    //...
}

Tip

Este método devuelve una lista de elementos.

Info

Para ver más sobre patrones en Redis ver la docu de redis

Obtener las keys de redis

Podemos obtener el listado de keys del servidor de redis con el método GetKeys

Eliminar elementos de redis

En caso de que no configuremos una expiración o quisiéramos eliminar antes de tiempo una key podemos utilizar los métodos:

void Remove(string key);

Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));

Task RemoveAllWithPatternAsync(string pattern = "*");

Como eliminar múltiples keys

Para eliminar múltiples keys debemos utilizar el método RemoveAllWithPatternAsync.

public async Task ExampleRemove()
{
    var pattern = "AlgunaKey:*";
    await _cache.RemoveAllWithPatternAsync(pattern);

    //...
}

Este método elimina todas las keys que cumplen con el Regex

UseDatabase

En el momento que tengamos que hacer algo muy particular con Redis podemos utilizar los métodos:

void UseDatabase(Action<IDatabase> action);

TResult UseDatabase<TResult>(Func<IDatabase, TResult> action) where TResult : class;

Estos métodos inyectan la instancia de base de datos a una función lo que nos permite utilizar funciones nativas de Redis.

Danger

Este método es para último recurso, ya que el uso del mismo nos agrega dependencia directa con el server de redis lo que puede provocar un gran esfuerzo de migración o problemas en el mantenimiento de las aplicaciones.

Info

Pueden encontrar ejemplos en el repositorio de la librería architecture-it/ARQ.Redis