Interacción con Azure Cache for Redis mediante .NET

Completado

Normalmente, una aplicación cliente usa una biblioteca cliente para formar las solicitudes y ejecutar comandos en una caché de Redis. Puede obtener una lista de bibliotecas cliente directamente desde la página de clientes de Redis.

Ejecución de comandos en la caché de Redis

Un cliente popular de Redis de alto rendimiento para el lenguaje de .NET es StackExchange.Redis. El paquete está disponible a través de NuGet y se puede agregar al código de .NET mediante la línea de comandos o el IDE. A continuación se muestran algunos ejemplos de cómo usar el cliente.

Conexión a la caché de Redis con StackExchange.Redis

Recuerde que usamos la dirección del host, el número de puerto y una clave de acceso para conectarse a un servidor de Redis. Azure ofrece también una cadena de conexión para algunos clientes de Redis que empaquetan estos datos juntos en una sola cadena. Tiene un aspecto similar al siguiente (con los campos cache-name y password-here rellenos con valores reales):

[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False

Puede pasar esta cadena a StackExchange.Redis para crear una conexión al servidor.

Observe que al final hay dos parámetros más:

  • ssl: garantiza que la comunicación está cifrada.
  • abortConnection: permite crear una conexión incluso aunque el servidor no esté disponible en ese momento.

Existen varios parámetros opcionales que puede anexar a la cadena para configurar la biblioteca cliente.

Creación de una conexión

El objeto de conexión principal de StackExchange.Redis es la clase StackExchange.Redis.ConnectionMultiplexer. Este objeto resume el proceso de conexión a un servidor de Redis (o grupo de servidores). Se ha optimizado para administrar conexiones de forma eficaz y pretende conservarse mientras tenga acceso a la memoria caché.

Puede crear una instancia de ConnectionMultiplexer mediante ConnectionMultiplexer.Connect estático o mediante el método ConnectionMultiplexer.ConnectAsync, pasando una cadena de conexión o un objeto ConfigurationOptions.

A continuación se muestra un sencillo ejemplo:

using StackExchange.Redis;
...
var connectionString = "[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False";
var redisConnection = ConnectionMultiplexer.Connect(connectionString);

Una vez que tenga un ConnectionMultiplexer, hay tres cosas principales que es posible que quiera hacer:

  • Acceda a una base de datos de Redis. En esto es en lo que nos centraremos aquí.
  • Usar las características de publicador o de suscriptor de Redis. Esto está fuera del ámbito de este módulo.
  • Acceda a un servidor individual con fines de mantenimiento o supervisión.

Acceso a una base de datos de Redis

La base de datos de Redis está representado por el tipo IDatabase. Puede recuperar uno mediante el método GetDatabase():

IDatabase db = redisConnection.GetDatabase();

Sugerencia

El objeto devuelto desde GetDatabase es un objeto ligero y no necesita almacenarse. Solo necesita que ConnectionMultiplexer se mantenga activo.

Una vez que tenga un objeto IDatabase, puede ejecutar métodos para interactuar con la memoria caché. Todos los métodos tienen versiones sincrónicas y asincrónicas que devuelven objetos Task para que sean compatibles con las palabras clave async y await.

A continuación, un ejemplo de almacenamiento de un par clave-valor en la caché:

bool wasSet = db.StringSet("favorite:flavor", "i-love-rocky-road");

El método StringSet devuelve un bool que indica si se ha establecido el valor (true) o no (false). Se puede recuperar el valor con el método StringGet:

string value = db.StringGet("favorite:flavor");
Console.WriteLine(value); // displays: ""i-love-rocky-road""

Obtención y establecimiento de valores binarios

Recuerde que las claves y valores de Redis son cadenas seguras binarias. Estos mismos métodos se pueden usar para almacenar datos binarios. Existen operadores de conversión implícitos que funcionan con tipos byte[] para que pueda trabajar de forma natural con los datos:

byte[] key = ...;
byte[] value = ...;

db.StringSet(key, value);
byte[] key = ...;
byte[] value = db.StringGet(key);

StackExchange.Redis representa claves mediante el tipo RedisKey. Esta clase tiene conversiones implícitas hacia y desde string y byte[], lo que permite a las claves de texto y las claves binarias utilizarse sin ninguna complicación. Los valores se representan mediante el tipo RedisValue . Al igual que con RedisKey, hay conversiones implícitas en marcha que le permiten pasar string o byte[].

Otras operaciones habituales

La interfaz IDatabase incluye otros métodos para trabajar con la caché de Redis. Hay métodos para trabajar con hashes, listas, conjuntos y conjuntos ordenados.

Estos son algunos de los más comunes que funcionan con claves sencillas, puede leer el código fuente de la interfaz para ver la lista completa.

Método Descripción
CreateBatch Crea un grupo de operaciones que se envía al servidor como una sola unidad, pero que no necesariamente se procesa como una unidad.
CreateTransaction Crea un grupo de operaciones que se envía al servidor como una sola unidad y que se procesa como tal.
KeyDelete Elimine el par clave/valor.
KeyExists Devuelve si la clave especificada existe o no en la memoria caché.
KeyExpire Establece una expiración del período de vida (TTL) en una clave.
KeyRename Cambia el nombre de una clave.
KeyTimeToLive Devuelve el período de vida de una clave.
KeyType Devuelve la representación de cadena del tipo del valor almacenado en la clave. Los distintos tipos que se pueden devolver son: cadena, lista, conjunto, zset y hash.

Ejecución de otros comandos

El objeto IDatabase tiene un método Execute y ExecuteAsync que se puede usar para pasar comandos textuales al servidor de Redis. Por ejemplo:

var result = db.Execute("ping");
Console.WriteLine(result.ToString()); // displays: "PONG"

Los métodos Execute y ExecuteAsync devuelven un objeto RedisResult que es un contenedor de datos que incluye dos propiedades:

  • Type que devuelve un string que indica el tipo del resultado: "STRING", "INTEGER", etc.
  • IsNull un valor true o false para detectar cuándo el resultado es null.

Después, puede usar ToString() en RedisResult para obtener el valor devuelto real.

Puede usar Execute para realizar cualquiera de los comandos admitidos: por ejemplo, podemos obtener todos los clientes conectados a la caché ("CLIENT LIST"):

var result = await db.ExecuteAsync("client", "list");
Console.WriteLine($"Type = {result.Type}\r\nResult = {result}");

Esto generaría como salida todos los clientes conectados:

Type = BulkString
Result = id=9469 addr=16.183.122.154:54961 fd=18 name=DESKTOP-AAAAAA age=0 idle=0 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=subscribe numops=5
id=9470 addr=16.183.122.155:54967 fd=13 name=DESKTOP-BBBBBB age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=client numops=17

Almacenamiento de valores más complejos

Redis está orientado en torno a cadenas seguras binarias pero puede almacenar en caché los gráficos de objeto serializándolos en un formato textual: normalmente XML o JSON. Por ejemplo, quizás para nuestras estadísticas, tenemos un objeto GameStats con el siguiente aspecto:

public class GameStat
{
    public string Id { get; set; }
    public string Sport { get; set; }
    public DateTimeOffset DatePlayed { get; set; }
    public string Game { get; set; }
    public IReadOnlyList<string> Teams { get; set; }
    public IReadOnlyList<(string team, int score)> Results { get; set; }

    public GameStat(string sport, DateTimeOffset datePlayed, string game, string[] teams, IEnumerable<(string team, int score)> results)
    {
        Id = Guid.NewGuid().ToString();
        Sport = sport;
        DatePlayed = datePlayed;
        Game = game;
        Teams = teams.ToList();
        Results = results.ToList();
    }

    public override string ToString()
    {
        return $"{Sport} {Game} played on {DatePlayed.Date.ToShortDateString()} - " +
               $"{String.Join(',', Teams)}\r\n\t" + 
               $"{String.Join('\t', Results.Select(r => $"{r.team } - {r.score}\r\n"))}";
    }
}

Podríamos usar la biblioteca Newtonsoft.Json para convertir una instancia de este objeto en una cadena:

var stat = new GameStat("Soccer", new DateTime(2019, 7, 16), "Local Game", 
                new[] { "Team 1", "Team 2" },
                new[] { ("Team 1", 2), ("Team 2", 1) });

string serializedValue = Newtonsoft.Json.JsonConvert.SerializeObject(stat);
bool added = db.StringSet("event:1950-world-cup", serializedValue);

Podríamos recuperarla y hacer que vuelva a ser un objeto mediante el proceso inverso:

var result = db.StringGet("event:2019-local-game");
var stat = Newtonsoft.Json.JsonConvert.DeserializeObject<GameStat>(result.ToString());
Console.WriteLine(stat.Sport); // displays "Soccer"

Limpieza de la conexión

Una vez que haya terminado con la conexión de Redis, puede DisposeConnectionMultiplexer. Esto cierra todas las conexiones y apaga la comunicación con el servidor.

redisConnection.Dispose();
redisConnection = null;