探索用于与 Redis 交互的客户端 API

已完成

如前所述,Redis 是一个内存中 NoSQL 数据库,它可跨多个服务器进行复制。 它通常用作缓存,但也可以用作正式数据库甚至消息中转站。

Redis 可以存储各种数据类型和结构。 它支持可发出的各种命令,以检索缓存数据或查询关于缓存本身的信息。 处理的数据始终以键/值对的形式存储。

针对 Redis 缓存执行命令

客户端应用程序通常使用客户端库来构建请求以及针对 Redis 缓存执行命令。 可以直接从 Redis 客户端页获取客户端库列表。 StackExchange.Redis 是面向 .NET 平台的常用高性能 Redis 客户端。 可以通过 NuGet 获取该包,并可以使用命令行或 IDE 将其添加到 .NET 代码。

使用 StackExchange.Redis 连接到 Redis 缓存

回顾前面的课程,我们曾经使用主机地址、端口号和访问密钥连接到 Redis 服务器。 Azure 还为一些 Redis 客户端提供连接字符串,将这些数据一起捆绑成单个字符串。

什么是连接字符串?

连接字符串是单个文本行,其中包含连接和验证 Azure 中的 Redis 缓存时所需的全部信息片段。 连接字符串如下所示(cache-namepassword-here 字段中应填写实际值):

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

提示

应在应用程序中保护连接字符串。 如果应用程序托管在 Azure 上,请考虑使用 Azure Key Vault 来存储值。

可将此字符串传递到 StackExchange.Redis,以便与服务器建立连接。

请注意末尾有两个附加参数:

  • ssl:确保通信加密
  • abortConnection:即使服务器当时不可用,也能创建连接

可在该字符串的后面追加其他几个可选参数,以配置客户端库。

创建连接

StackExchange.Redis 中的主连接对象是 StackExchange.Redis.ConnectionMultiplexer 类。 此对象将连接到 Redis 服务器(或服务器组)的过程抽象化。 此对象经过优化,可以有效管理连接,每当访问缓存时,都需要准备好它。

使用静态 ConnectionMultiplexer.ConnectConnectionMultiplexer.ConnectAsync 方法并传入连接字符串或 ConfigurationOptions 对象来创建 ConnectionMultiplexer 实例。

下面是一个简单示例:

using StackExchange.Redis;
...
var connectionString = "[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False";
var redisConnection = ConnectionMultiplexer.Connect(connectionString);
    // ^^^ store and re-use this!!!

创建 ConnectionMultiplexer 后,需要执行三项主要操作:

  1. 访问 Redis 数据库(本模块主旨)。
  2. 利用 Redis 的发布者/下标功能(不在本模块范围内)。
  3. 访问单个服务器以进行维护或监视。

访问 Redis 数据库

Redis 数据库由 IDatabase 类型表示。 可以使用 GetDatabase() 方法检索 Redis 数据库:

IDatabase db = redisConnection.GetDatabase();

提示

GetDatabase 返回的对象是一个轻型对象,不需要存储它。 只需将 ConnectionMultiplexer 保持活动状态。

获取 IDatabase 对象后,可以执行与缓存交互的方法。 所有方法都提供同步和异步版本,这些版本返回 Task 对象,因此与 asyncawait 关键字兼容。

下面是在缓存中存储键/值的示例:

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

StringSet 方法返回 bool,指示是已设置 (true) 还是未设置 (false) 该值。 然后,可以使用 StringGet 方法检索该值:

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

获取和设置二进制值

回顾前面的课程,Redis 键和值是二进制安全的。 可以使用相同的方法存储二进制数据。 可以使用隐式转换运算符来处理 byte[] 类型,以便可以像平时一样处理数据:

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

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

提示

StackExchange.Redis 使用 RedisKey 类型表示键。 此类提供与 stringbyte[] 的相互隐藏转换,使我们能够十分轻松地使用文本和二进制键。 值由 RedisValue 类型表示。 与 RedisKey 一样,可以通过隐式转换来传递 stringbyte[]

其他常见操作

IDatabase 接口包含其他几个方法用于处理 Redis 缓存。 还有一些方法可以处理哈希、列表、集和有序集。

下面是用于处理单个键的其他一些常见操作。若要查看完整列表,可以阅读该接口的源代码

方法 说明
CreateBatch 创建要作为单个单元发送到服务器、但不一定要作为单个单元进行处理的一组操作。
CreateTransaction 创建要作为单个单元发送到服务器、并在服务器上作为单个单元进行处理的一组操作。
KeyDelete 删除键/值。
KeyExists 返回给定的键是否在缓存中存在的结果。
KeyExpire 针对某个键设置生存时间 (TTL) 过期时间。
KeyRename 重命名某个键。
KeyTimeToLive 返回键的 TTL。
KeyType 返回键中存储的值类型的字符串表示形式。 可返回的不同类型包括:字符串、列表、集、zset 和哈希。

执行其他命令

IDatabase 对象包含可用于将文本命令传递给 Redis 服务器的 ExecuteExecuteAsync 方法。 例如:

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

ExecuteExecuteAsync 方法返回 RedisResult 对象,该对象是包含以下两个属性的数据保存器:

  • Type:返回指示结果类型(“STRING”、“INTEGER”等)的 string
  • IsNull:结果为 null 时要检测的 true/false 值。

然后,可对 RedisResult 使用 ToString() 来获取实际返回值。

可使用 Execute 执行任何受支持的命令。 例如,可以获取已连接到缓存的所有客户端(“CLIENT LIST”):

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

上述命令输出所有连接的客户端:

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

存储更复杂的值

Redis 面向二进制安全的字符串,但你可以通过将对象图序列化为文本格式(通常是 XML 或 JSON)来缓存这些图形。 例如,对于统计信息,我们也许可以创建如下所示的 GameStat 对象:

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

可以使用 Newtonsoft.Json 库将此对象的实例转换为字符串:

var stat = new GameStat("Soccer", new DateTime(1950, 7, 16), "FIFA World Cup", 
                new[] { "Uruguay", "Brazil" },
                new[] { ("Uruguay", 2), ("Brazil", 1) });

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

可以检索该字符串,然后使用相反的过程将它还原为对象:

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

清理连接

完成 Redis 连接后,可以处置 ConnectionMultiplexer。 此命令会关闭所有连接,并关闭与服务器的通信:

redisConnection.Dispose();
redisConnection = null;

让我们创建一个应用程序,然后使用 Redis 缓存完成一些工作。

如前所述,Redis 是一个内存中 NoSQL 数据库,它可跨多个服务器进行复制。 它通常用作缓存,但也可以用作正式数据库甚至消息中转站。

Redis 可以存储各种数据类型和结构。 它支持可发出的各种命令,以检索缓存数据或查询关于缓存本身的信息。 处理的数据始终以键/值对的形式存储。

针对 Redis 缓存执行命令

客户端应用程序通常使用客户端库来构建请求以及针对 Redis 缓存执行命令。 可以直接从 Redis 客户端页获取客户端库列表。 redis 包是适合 JavaScript 的常用 Redis 客户端,可通过 npm install redis 将它添加到项目中。

使用 redis 包连接到 Redis 缓存

通过 RedisClient 类可实现与 Redis 缓存的交互。 在大多数情况下,最好使用样本代码创建一个 RedisClient,它会连接到 Azure 中的 Redis 缓存:

const client = redis.createClient(
  port,  // the port number, 6380 by default
  hostname,  // <resourceName>.redis.cache.windows.net
  {
    password: accessKey,  // the primary or secondary access key
    tls: { servername: hostname }
  }
);

在多数情况下,应避免创建多个 RedisClient。 可在代码中需要 Redis 的任何位置传递和使用 RedisClient 的单个实例。

使用 Redis 数据库

Redis 命令表示为 RedisClient 上的方法,其名称与命令本身相同。 以下示例展示了缓存中新值的存储:

client.set("myKey", "myValue"); // executes "set myKey myValue" on the cache

RedisClient 上的所有命令方法都是异步的,并且支持能提供结果的可选回调参数。 redis 包并非开箱即可支持承诺(因此,也不支持 async/await 或带 .then() 的链接)。 要通过 RedisClient 使用 async/await.then(),最简单的方法是通过 bluebird 包的 promisifyAll 函数一次性向整个客户端添加承诺支持:

var redis = require("redis");
var Promise = require("bluebird");

Promise.promsifyAll(redis);

promisifyAll 函数会将 XXXAsync 版本的所有命令方法添加到 RedisClient 实例,从而能够支持使用异步方法,如以下示例所示:

var result = await client.setAsync("myKey", "myValue");

动态执行命令

可以使用 sendCommand()(或带有 bluebird 的 sendCommandAsync())以动态的方式发送命令,将任何字符串作为命令发送到缓存。 例如,应用可能会提示将命令直接发送到缓存,或者 Redis 可能会引入 redis 包不支持的新命令。 必须以数组的形式发送命令参数。

// Add a key/value pair
var result = await client.sendCommandAsync("set", ["myKey", "myValue"]);

清理连接

完成 Redis 连接后,应使用 quit() 将其关闭(使用 bluebird 时,采用 quitAsync()):

await client.quitAsync();

让我们创建一个应用程序,然后使用 Redis 缓存完成一些工作。