Interagir com o Cache Redis do Azure usando o .NET

Concluído

Normalmente, um aplicativo cliente usa uma biblioteca de cliente para formar solicitações e executar comandos em um cache Redis. Pode obter uma lista de bibliotecas de cliente diretamente na página de clientes do Redis.

Executar comandos na cache de Redis

Um cliente do Redis popular de desempenho elevado para a linguagem .NET é o StackExchange.Redis. O pacote está disponível através de NuGet e pode ser adicionado ao seu código .NET através da linha de comandos ou do IDE. Seguem-se exemplos de como utilizar o cliente.

Ligar à cache de Redis com StackExchange.Redis

Lembre-se de que utilizamos o endereço do anfitrião, o número de porta e uma chave de acesso para ligar a um servidor Redis. O Azure também oferece uma cadeia de conexão para alguns clientes Redis que agrupa esses dados em uma única cadeia de caracteres. Tem a seguinte aparência (com os campos e password-here preenchidos cache-name com valores reais):

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

Pode transmitir esta cadeia para StackExchange.Redis para criar uma ligação ao servidor.

Observe que há mais dois parâmetros no final:

  • ssl - garante que a comunicação é encriptada.
  • abortConnection - permite criar uma ligação, mesmo que o servidor não esteja disponível nesse momento.

Pode anexar vários outros parâmetros opcionais à cadeia para configurar a biblioteca de cliente.

Criar uma ligação

O objeto de cadeia principal em StackExchange.Redis é a classe StackExchange.Redis.ConnectionMultiplexer. Este objeto abstrai o processo de ligação a um servidor Redis (ou a um grupo de servidores). Está otimizado para gerir as ligações de forma eficiente e deve permanecer por perto enquanto precisa de acesso à cache.

Para criar uma instância ConnectionMultiplexer, vai utilizar o ConnectionMultiplexer.Connect estático ou o método ConnectionMultiplexer.ConnectAsync e transmitir uma cadeia de ligação ou um objeto ConfigurationOptions.

Eis um exemplo simples:

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

Depois de ter um ConnectionMultiplexer, há três coisas principais que você pode querer fazer:

  • Aceder a uma Base de Dados do Redis. É sobre isto que nos vamos debruçar aqui.
  • Faça uso dos recursos de editor/assinante do Redis. Este ponto está fora do âmbito deste módulo.
  • Aceder a um servidor individual para fins de manutenção ou monitorização.

Aceder a uma base de dados Redis

A base de dados do Redis é representada pelo tipo IDatabase. Pode obter uma com o método GetDatabase():

IDatabase db = redisConnection.GetDatabase();

Gorjeta

O objeto devolvido de GetDatabase é leve e não tem de ser armazenado. Só é necessário manter o objeto ConnectionMultiplexer ativo.

Quando tiver um objeto IDatabase, pode executar métodos para interagir com a cache. Todos os métodos têm versões síncronas e assíncronas que retornam Task objetos para torná-los compatíveis com as async palavras-chave e await .

Segue-se um exemplo de armazenamento de uma chave/valor na cache:

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

O método StringSet devolve um bool, que indica se o valor foi definido (true) ou não (false). Depois, podemos obter o valor com o método StringGet:

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

Obter e definir os valores binários

Lembre-se de que as chaves e os valores de Redis são binary safe. Estes mesmos métodos podem ser utilizados para armazenar dados binários. Existem operadores de conversão implícitos que funcionam com os tipos byte[], para que possa trabalhar com os dados naturalmente:

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

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

StackExchange.Redis utiliza o tipo RedisKey para representar chaves. Esta classe tem conversões implícitas para e de string e byte[], permitindo a utilização de texto e chaves binárias sem qualquer complicação. Os valores são representados pelo tipo RedisValue . Tal como com o tipo RedisKey, existem conversões implícitas que lhe permitem transmitir string ou byte[].

Outras operações comuns

A interface IDatabase inclui vários outros métodos para trabalhar com a cache de Redis. Há métodos para trabalhar com hashes, listas, conjuntos e conjuntos ordenados.

Eis alguns dos mais comuns que funcionam com chaves únicas; pode ler o código de origem da interface para obter a lista completa.

Método Descrição
CreateBatch Cria um grupo de operações que será enviado para o servidor como uma única unidade, mas não processado como tal necessariamente.
CreateTransaction Cria um grupo de operações que será enviado para o servidor como uma única unidade e processado como tal.
KeyDelete Eliminar a chave/valor.
KeyExists Devolve se a chave específica existe na cache.
KeyExpire Define uma expiração de time-to-live (TTL) numa chave.
KeyRename Muda o nome de uma chave.
KeyTimeToLive Devolve o TTL de uma chave.
KeyType Devolve a representação da cadeia do tipo do valor armazenado na chave. Podem ser utilizados os tipos cadeia, lista, conjunto, zset e hash.

Executar outros comandos

O IDatabase objeto tem um Execute método e ExecuteAsync que pode ser usado para passar comandos textuais para o servidor Redis. Por exemplo:

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

Os Execute métodos e ExecuteAsync retornam um objeto que é um RedisResult titular de dados que inclui duas propriedades:

  • Type que retorna um string indicando o tipo do resultado - "STRING", "INTEGER", etc.
  • IsNull, um valor verdadeiro/falso para detetar quando o resultado for null.

Pode utilizar ToString() em RedisResult para obter o valor de retorno real.

Pode utilizar Execute para realizar qualquer comando suportado; por exemplo, podemos obter todos os clientes ligados à cache ("CLIENT LIST"):

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

Como resultado, são apresentados os clientes ligados:

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

Armazenar valores mais complexos

O Redis foi concebido em torno de cadeias “binary safe”, mas pode serializar os grafo de objetos num formato textual, geralmente XML ou JSON, e retirá-los da cache. Por exemplo, talvez para as nossas estatísticas, temos um GameStats objeto que se parece com:

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

Podemos utilizar a biblioteca Newtonsoft.Json para transformar uma instância deste objeto numa cadeia:

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

Podemos utilizar o processo inverso e obtê-la e transformá-la de novo num objeto.

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

Limpar a ligação

Depois de terminar a conexão Redis, você pode Dispose o ConnectionMultiplexer. Isso fecha todas as conexões e desliga a comunicação com o servidor.

redisConnection.Dispose();
redisConnection = null;