Interagire con la cache di Azure per Redis usando .NET

Completato

Un'applicazione client usa in genere una libreria client per formare richieste ed eseguire comandi in una cache Redis. È possibile ottenere un elenco di librerie client direttamente dalla pagina dei client Redis.

Esecuzione di comandi nella cache Redis

Un client Redis ad alte prestazioni tra i più diffusi per il linguaggio .NET è StackExchange.Redis. Il pacchetto è disponibile tramite NuGet e può essere aggiunto al codice .NET tramite la riga di comando o l'ambiente di sviluppo integrato. Di seguito vengono mostrati alcuni esempi di come usare il client.

Connessione alla cache Redis con StackExchange.Redis

È importante ricordare che per la connessione a un server Redis vengono usati l'indirizzo dell'host, il numero della porta e una chiave di accesso. Azure offre anche per alcuni client Redis una stringa di connessione che unisce questi dati in una singola stringa. Avrà un aspetto simile al seguente, con i campi cache-name e password-here compilati con valori effettivi:

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

È possibile passare questa stringa a StackExchange.Redis per creare una connessione al server.

Si noti che ala fine sono presenti due altri parametri:

  • ssl: assicura che le comunicazioni siano crittografate.
  • abortConnection: consente la creazione di una connessione anche se il server non è disponibile in quel momento.

Sono disponibili altri parametri facoltativi da aggiungere alla fine della stringa per configurare la libreria client.

Creazione di una connessione

L'oggetto principale della connessione in StackExchange.Redis è la classe StackExchange.Redis.ConnectionMultiplexer. Questo oggetto astrae il processo di connessione a un server Redis o a un gruppo di server. È ottimizzato per gestire le connessioni in modo efficiente e deve essere disponibile quando è necessario accedere alla cache.

È possibile creare un'istanza di ConnectionMultiplexer usando il metodo statico ConnectionMultiplexer.Connect o ConnectionMultiplexer.ConnectAsync, passando una stringa di connessione o un oggetto ConfigurationOptions.

Ecco un semplice esempio:

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

Quando è disponibile un'istanza di ConnectionMultiplexer, è necessario eseguire tre operazioni principali:

  • Accedere a un database Redis.
  • Usare le funzionalità editore/sottoscrittore di Redis, che non rientrano nell'ambito di questo modulo.
  • Accedere a un singolo server per finalità di manutenzione o monitoraggio.

Accesso a un database Redis

Il tipo IDatabase rappresenta il database Redis. È possibile recuperarne uno usando il metodo GetDatabase():

IDatabase db = redisConnection.GetDatabase();

Suggerimento

L'oggetto restituito da GetDatabase è un oggetto leggero e non è necessario archiviarlo. È necessario mantenere attivo solo ConnectionMultiplexer.

Quando l'oggetto IDatabase è disponibile, è possibile eseguire metodi per interagire con la cache. Tutti i metodi hanno versioni sincrone e asincrone che restituiscono oggetti Task per renderli compatibili con le parole chiave async e await.

Ecco un esempio di archiviazione di una coppia chiave/valore nella cache:

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

Il metodo StringSet restituisce un valore bool per indicare che il valore è stato impostato (true) o non è stato impostato (false). È quindi possibile recuperare il valore con il metodo StringGet:

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

Recupero e impostazione di valori binari

È importante ricordare che le chiavi e i valori di Redis sono indipendenti dall'aspetto binario. Gli stessi metodi possono essere usati per archiviare dati binari. Sono disponibili operatori di conversione impliciti da usare con i tipi byte[] in modo da potere usare i dati normalmente:

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

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

StackExchange.Redis rappresenta le chiavi tramite il tipo RedisKey. Questa classe include conversioni implicite verso e da string e byte[], consentendo l'uso di chiavi di testo e binarie senza complicazioni. I valori sono rappresentati dal tipo RedisValue . Analogamente a RedisKey, sono disponibili conversioni implicite che consentono di passare string o byte[].

Altre operazioni comuni

L'interfaccia IDatabase include alcuni metodi che consentono di usare la cache Redis. Sono disponibili metodi che consentono di usare hash, elenchi, set e set ordinati.

Ecco alcuni dei più comuni che possono essere usati con le chiavi singole. Per visualizzare l'elenco completo, è possibile leggere il codice sorgente per l'interfaccia.

Metodo Descrizione
CreateBatch Crea un gruppo di operazioni da inviare al server come singola unità, ma non necessariamente elaborate come unità.
CreateTransaction Crea un gruppo di operazioni da inviare al server come singola unità ed elaborate nel server come singola unità.
KeyDelete Elimina la coppia chiave/valore.
KeyExists Consente di restituire un valore che indica se la chiave specificata esiste nella cache.
KeyExpire Consente di impostare una scadenza TTL per una chiave.
KeyRename Consente di rinominare una chiave.
KeyTimeToLive Consente di restituire il valore TTL per una chiave.
KeyType Consente di restituire la rappresentazione di stringa del tipo del valore archiviato nella chiave. Possono essere restituiti tipi diversi, tra cui stringa, elenco, set, zset e hash.

Esecuzione di altri comandi

L'oggetto IDatabase dispone dei metodi Execute e ExecuteAsync che possono essere usati per passare comandi testuali al server Redis. Ad esempio:

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

I metodi Execute e ExecuteAsync restituiscono un oggetto RedisResult che è un contenitore di dati che include due proprietà:

  • Resp2Type che restituisce un string che indica il tipo del risultato: STRING, INTEGERe così via.
  • IsNull è un valore true/false che consente di rilevare quando il risultato è null.

È quindi possibile usare ToString() in RedisResult per ottenere il valore restituito effettivo.

È possibile usare Execute per eseguire qualsiasi comando supportato, ad esempio è possibile fare in modo che tutti i client siano connessi alla cache ("CLIENT LIST"):

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

In questo modo vengono restituiti tutti i client connessi:

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

Archiviazione di valori più complessi

Redis è basato su stringhe indipendenti dall'aspetto binario, ma è possibile memorizzare nella cache oggetti grafici mediante la serializzazione di tali oggetti in un formato testuale, in genere XML o JSON. Ad esempio, per le statistiche è possibile che sia presente un oggetto GameStats con un aspetto simile al seguente:

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

È possibile usare la libreria Newtonsoft.Json per trasformare un'istanza dell'oggetto in una stringa:

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

È possibile recuperarla e trasformarla di nuovo in un oggetto mediante il processo inverso:

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

Pulizia della connessione

Quando la connessione non è più necessaria, è possibile Dispose l'elemento ConnectionMultiplexer. L'eliminazione consente di chiudere tutte le connessioni e di arrestare le comunicazioni con il server.

redisConnection.Dispose();
redisConnection = null;