使用 .NET 与 Azure Cache for Redis 交互

已完成

客户端应用程序通常使用客户端库来构建请求以及针对 Redis 缓存执行命令。 可以直接从 Redis 客户端页获取客户端库列表。

针对 Redis 缓存执行命令

用于 .NET 语言的常用高性能 Redis 客户端是 StackExchange.Redis。 可通过 NuGet 获取该包,可使用命令行或 IDE 将其添加到 .NET 代码。 下面是如何使用客户端的示例。

使用 StackExchange.Redis 连接到 Redis 缓存

回顾前面的课程,我们曾经使用主机地址、端口号和访问密钥连接到 Redis 服务器。 Azure 还为某些 Redis 客户端提供连接字符串,将这些数据一起捆绑成单个字符串。 如下所示(cache-namepassword-here 字段中应填写实际值):

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

可将此字符串传递到 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);

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

  • 访问 Redis 数据库。
  • 利用 Redis 的发布者/订阅者功能(不在本模块范围内)。
  • 访问单个服务器以进行维护或监视。

访问 Redis 数据库

类型“IDatabase”表示 Redis 数据库。 可以使用 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 对象,该对象是包含以下两个属性的数据保存器:

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

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

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

var result = await db.ExecuteAsync("client", "list");
Console.WriteLine($"Type = {result.Resp2Type}\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)来缓存这些图形。 例如,对于统计信息,我们也许可以创建如下所示的 GameStats 对象:

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

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

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

清理连接

不再需要连接时,可以Dispose ConnectionMultiplexer。 这会关闭所有连接,并关闭与服务器的通信。

redisConnection.Dispose();
redisConnection = null;