Interagir com o Cache do Azure para Redis usando o .NET

Concluído

Normalmente, um aplicativo cliente usará uma biblioteca de clientes para formar solicitações e executar comandos em um Cache Redis. Você pode obter uma lista de bibliotecas de cliente diretamente na Página de clientes Redis.

Execução de comandos no cache Redis

Um cliente popular de Redis de alto desempenho para a linguagem .NET é StackExchange.Redis. O pacote está disponível por meio do NuGet e pode ser adicionado ao código .NET usando a linha de comando ou o IDE. Veja abaixo exemplos de como usar o cliente.

Conectar-se ao seu cache Redis com stackExchange.Redis

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

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

Você pode passar essa cadeia de caracteres para StackExchange.Redis a fim de criar uma conexão com o servidor.

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

  • ssl - garante que a comunicação seja criptografada.
  • abortConnection - permite a criação de uma conexão, mesmo se o servidor estiver indisponível no momento.

Há vários outros parâmetros opcionais que você pode acrescentar à cadeia de caracteres para configurar a biblioteca de clientes.

Criar uma nova conexão

O objeto de conexão principal no StackExchange.Redis é a classe StackExchange.Redis.ConnectionMultiplexer. Esse objeto abstrai o processo de conexão com um servidor Redis (ou grupo de servidores). Ele foi otimizado para gerenciar conexões com eficiência e deve ser mantido por perto enquanto você precisa ter acesso ao cache.

Crie uma instância de ConnectionMultiplexer usando o método estático ConnectionMultiplexer.Connect ou ConnectionMultiplexer.ConnectAsync, passando uma cadeia de conexão ou um objeto ConfigurationOptions.

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

Quando você tiver um ConnectionMultiplexer, há três coisas principais que talvez você queira fazer:

  • Acessar um banco de dados Redis.
  • Usar os recursos publicador/assinante do Redis, que está fora do escopo deste módulo.
  • Acessar um servidor individual para manutenção ou monitoramento.

Acessar um banco de dados Redis

O tipo IDatabase representa o banco de dados Redis. Você pode recuperar um usando o método GetDatabase():

IDatabase db = redisConnection.GetDatabase();

Dica

O objeto retornado por GetDatabase é um objeto leve, e não precisa ser armazenado. Somente o ConnectionMultiplexer precisa ser mantido ativo.

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

A seguir está um exemplo de como armazenar uma chave/valor no cache:

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

O método StringSet retorna um bool indicando se o valor foi definido (true) ou não (false). Em seguida, recuperamos o valor com o método StringGet:

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

Obter e definir valores binários

Lembre-se de que as chaves e valores de Redis são válidos para binários. Esses mesmos métodos podem ser usados para armazenar dados binários. Há operadores de conversão implícitos para trabalhar com tipos byte[], para que você possa trabalhar naturalmente com os dados:

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

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

StackExchange.Redis representa chaves usando o tipo RedisKey. Essa classe tem conversões implícitas de string e byte[], permitindo que as chaves de texto e binário sejam usadas sem qualquer complicação. Os valores são representados pelo tipo RedisValue . Assim como acontece com RedisKey, há conversões implícitas em vigor para permitir que você passe string ou byte[].

Outras operações comuns

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

Estes são alguns dos mais comuns que funcionam com chaves únicas. É possível ler o código-fonte da interface ver a lista completa.

Método Descrição
CreateBatch Cria um grupo de operações a serem enviadas ao servidor como uma única unidade, mas não necessariamente processadas como uma unidade.
CreateTransaction Cria um grupo de operações a serem enviadas ao servidor como uma única unidade e processadas no servidor como uma única unidade.
KeyDelete Exclua a chave/valor.
KeyExists Retorna se a chave especificada existe no cache.
KeyExpire Define uma expiração de TTL (vida útil) em uma chave.
KeyRename Renomeia uma chave.
KeyTimeToLive Retorna a TTL de uma chave.
KeyType Retorna a representação de cadeia de caracteres do tipo do valor armazenado na chave. Os diferentes tipos para retorno são: cadeia de caracteres, lista, conjunto, zset e hash.

Executar outros comandos

O objeto IDatabase tem um método Execute 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 métodos Execute e ExecuteAsync retornam um objeto RedisResult que é um detentor de dados que inclui duas propriedades:

  • Resp2Type que retorna um string indicando o tipo do resultado – STRING, INTEGERetc.
  • IsNull um valor true/false para detectar quando o resultado é null.

Depois, você pode usar ToString() no RedisResult para obter o valor de retorno real.

Use Execute para executar qualquer comando de suporte, por exemplo, todos os clientes podem ficar conectados ao cache ("LISTA DE CLIENTES"):

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

Isso gera todos os 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

Armazenar valores mais complexos

Redis é orientado por cadeias de caracteres binárias seguras, mas você pode armazenar em cache grafos de objeto serializando-os para um formato textual, normalmente XML ou JSON. Por exemplo, talvez para as nossas estatísticas, temos um objeto GameStats 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"))}";
    }
}

Poderíamos usar a biblioteca Newtonsoft.Json para transformar uma instância desse objeto em uma cadeia de caracteres:

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

Poderíamos recuperá-la e transformá-la de volta em um objeto usando o 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"

Limpar a conexão

Quando a conexão não for mais necessária, você poderá Dispose o ConnectionMultiplexer. Isso fechará todas as conexões e desligará a comunicação com o servidor.

redisConnection.Dispose();
redisConnection = null;