概览 - Snet Docs

🗄️ Redis 概述

Snet.Redis 是一个高性能 Redis 客户端库,基于 StackExchange.Redis 封装,面向 .NET 8/10。它提供了统一的 API,覆盖全部 5 种 Redis 数据类型(String、List、Hash、SortedSet、Key)以及发布/订阅功能。主要特性包括 TAG 前缀命名空间、对象存储的 JSON 序列化同步/异步双模 API连接事件监控以及基于 Lua 脚本的 通配符键删除

属性
包名 Snet.Redis
命名空间 Snet.Redis
目标框架 .NET 8.0 / .NET 10.0
依赖项 StackExchange.Redis 3.0.17, Snet.Core
许可证 MIT

▶️ 快速开始

安装包:

dotnet add package Snet.Redis

以下示例涵盖连接、字符串操作、对象序列化与断开连接:

using Snet.Redis;

var redis = RedisOperate.Instance(new RedisData.Basics
{
    ConnectStr = "127.0.0.1:6379",
    DataBaseID = 0,
    Expiry = 86400000,     // 24 小时(毫秒)
    TAG = "MyApp:"
});

// 连接
var result = await redis.OnAsync();
if (!result.Status) { Console.WriteLine($"连接失败: {result.Message}"); return; }

// 字符串操作
await redis.StringSetAsync("greeting", "Hello Redis!");
var value = await redis.StringGetAsync("greeting");
Console.WriteLine(value); // "Hello Redis!"

// 对象序列化
await redis.StringSetAsync("user:1", new { Name = "John", Age = 30 });
var user = await redis.StringGetAsync<User>("user:1");
Console.WriteLine($"{user.Name}, {user.Age}");

// 断开连接
await redis.OffAsync();

⚙️ 安装与配置

通过 RedisData.Basics 类进行配置:

参数 类型 默认值 描述
ConnectStr string Redis 连接字符串(如 "127.0.0.1:6379"
DataBaseID int 0 Redis 数据库编号(0-15)
Expiry int 86400000 默认 TTL(毫秒),24 小时
TAG string "S:" 键前缀,用于命名空间隔离

连接字符串格式:

// 本地
"127.0.0.1:6379"

// 远程 + 密码
"remote-host:6379,password=xxx"

// 哨兵模式
"sentinel-host:26379,serviceName=mymaster"

// 多端点
"host1:6379,host2:6379,host3:6379"

🧠 核心概念

1. TAG 前缀系统

所有键都会通过内部的 GTAG() 方法自动添加配置的 TAG 前缀,防止共享同一 Redis 实例的应用之间发生键冲突。

// 当 TAG = "MyApp:"
await redis.StringSetAsync("user:1", "Alice");
// 实际 Redis 键: "MyApp:user:1"

2. JSON 序列化

对象使用 value.ToJson()(来自 Snet.Core)存储,并通过 JsonSerializer.Deserialize<T>() 读取。这允许无缝存储复杂的 .NET 对象,无需手动序列化。

await redis.StringSetAsync("user:1", new User { Name = "John", Age = 30 });
var user = await redis.StringGetAsync<User>("user:1");

3. 同步/异步双模 API

每个操作都有同步和异步两种变体:

// 同步
redis.StringSet("key", "value");
var val = redis.StringGet("key");

// 异步
await redis.StringSetAsync("key", "value");
var val = await redis.StringGetAsync("key");

4. 单例模式

RedisOperate 内部使用 CoreUnify<T, B>.Instance(),确保每个唯一配置对应一个连接池。重用相同配置会返回同一实例。

5. 连接事件

库暴露七种连接事件,均通过 OnInfoEventHandler 上报:

事件 描述
ConnectionRestored 丢失的连接重新建立时触发
ConnectionFailed 连接尝试失败时触发
ErrorMessage Redis 错误消息时触发
ConfigurationChanged 主/副本拓扑变更时触发
HashSlotMoved 哈希槽迁移时触发
InternalError 库内部错误时触发
ConfigurationChangedBroadcast 广播配置变更时触发
redis.OnInfoEventHandler += (sender, args) =>
{
    Console.WriteLine($"[{args.EventType}] {args.Message}");
};

6. 断开连接时的返回值约定

当 Redis 不可达时,方法返回安全的默认值:

返回类型 默认值
bool false
long -1
string? null
T? default
IEnumerable? null

📚 API 参考

生命周期方法

方法 签名 描述
On OperateResult On() 同步连接
OnAsync Task<OperateResult> OnAsync(CancellationToken token = default) 异步连接
Off OperateResult Off(bool hardClose = false) 同步断开连接
OffAsync Task<OperateResult> OffAsync(bool hardClose = false, CancellationToken token = default) 异步断开连接
GetStatus OperateResult GetStatus() 获取连接状态
Dispose void Dispose() 清理资源
DisposeAsync ValueTask DisposeAsync() 异步清理资源

字符串操作(10 个方法)

方法 返回 描述
StringSet(key, value) bool 存储字符串值
StringSetAsync(key, value) Task<bool> 异步存储字符串
StringGet(key) string? 获取字符串值
StringGetAsync(key) Task<string?> 异步获取字符串
StringSet<T>(key, value) bool 以 JSON 格式存储对象
StringSetAsync<T>(key, value) Task<bool> 异步以 JSON 格式存储对象
StringGet<T>(key) T? 获取并反序列化对象
StringGetAsync<T>(key) Task<T?> 异步获取并反序列化对象
StringSet(kvPairs) bool 批量存储键值对
StringSetAsync(kvPairs) Task<bool> 异步批量存储

列表操作(22 个方法)

方法 返回 描述
ListLeftPush(key, value) long 将值推入列表头部
ListLeftPushAsync(key, value) Task<long> 异步推入头部
ListRightPush(key, value) long 将值推入列表尾部
ListRightPushAsync(key, value) Task<long> 异步推入尾部
ListLeftPop(key) string? 从头部弹出值
ListLeftPopAsync(key) Task<string?> 异步从头部弹出
ListRightPop(key) string? 从尾部弹出值
ListRightPopAsync(key) Task<string?> 异步从尾部弹出
ListLeftPush<T>(key, value) long 将序列化对象推入头部
ListLeftPushAsync<T>(key, value) Task<long> 异步推入序列化对象到头部
ListRightPush<T>(key, value) long 将序列化对象推入尾部
ListRightPushAsync<T>(key, value) Task<long> 异步推入序列化对象到尾部
ListLeftPop<T>(key) T? 从头部弹出并反序列化
ListLeftPopAsync<T>(key) Task<T?> 异步从头部弹出并反序列化
ListRightPop<T>(key) T? 从尾部弹出并反序列化
ListRightPopAsync<T>(key) Task<T?> 异步从尾部弹出并反序列化
ListRemove(key, value) long 从列表中移除值
ListRemoveAsync(key, value) Task<long> 异步移除值
ListLength(key) long 获取列表长度
ListLengthAsync(key) Task<long> 异步获取列表长度
ListRange(key, start, stop) IEnumerable<string?> 获取范围内的值
ListRangeAsync(key, start, stop) Task<IEnumerable<string?>> 异步获取范围内的值

哈希操作(18 个方法)

方法 返回 描述
HashExists(key, hashField) bool 检查哈希字段是否存在
HashExistsAsync(key, hashField) Task<bool> 异步检查字段存在性
HashDelete(key, hashField) bool 删除哈希字段
HashDeleteAsync(key, hashField) Task<bool> 异步删除字段
HashSet(key, hashField, value) bool 设置哈希字段值
HashSetAsync(key, hashField, value) Task<bool> 异步设置字段值
HashGet(key, hashField) string? 获取哈希字段值
HashGetAsync(key, hashField) Task<string?> 异步获取字段值
HashSet<T>(key, hashField, value) bool 设置序列化对象作为字段值
HashSetAsync<T>(key, hashField, value) Task<bool> 异步设置序列化字段
HashGet<T>(key, hashField) T? 获取并反序列化字段值
HashGetAsync<T>(key, hashField) Task<T?> 异步获取反序列化字段
HashKeys(key) IEnumerable<string?> 获取所有哈希字段名
HashKeysAsync(key) Task<IEnumerable<string?>> 异步获取字段名
HashValues(key) IEnumerable<string?> 获取所有哈希值
HashValuesAsync(key) Task<IEnumerable<string?>> 异步获取值

有序集合操作(10 个方法)

方法 返回 描述
SortedSetAdd(key, member, score) bool 添加带分数的成员
SortedSetAddAsync(key, member, score) Task<bool> 异步添加成员
SortedSetRemove(key, member) bool 移除成员
SortedSetRemoveAsync(key, member) Task<bool> 异步移除成员
SortedSetRangeByRank(key, start, stop) IEnumerable<string> 按排名范围获取成员
SortedSetRangeByRankAsync(key) Task<IEnumerable<string?>> 异步按排名获取所有成员
SortedSetLength(key) long 获取集合基数
SortedSetLengthAsync(key) Task<long> 异步获取基数
SortedSetIncrement(key, member, value) double 增加成员分数
SortedSetIncrementAsync(key, member, value) Task<double> 异步增加分数

键操作(8 个方法)

方法 返回 描述
KeyDelete(key) bool 删除键(通过 Lua 支持 * 通配符)
KeyDeleteAsync(key) Task<bool> 异步删除键
KeyExists(key) bool 检查键是否存在
KeyExistsAsync(key) Task<bool> 异步检查键存在性
KeyRename(key, newKey) bool 重命名键
KeyRenameAsync(key, newKey) Task<bool> 异步重命名键
KeyExpire(key, expiry) bool 设置键的 TTL
KeyExpireAsync(key, expiry) Task<bool> 异步设置 TTL

发布/订阅操作(6 个方法)

方法 返回 描述
Subscribe(channel, handler) void 订阅频道
SubscribeAsync(channel, handler) Task 异步订阅
Publish(channel, message) long 发布消息
PublishAsync(channel, message) Task<long> 异步发布
Publish<T>(channel, message) long 发布序列化对象
PublishAsync<T>(channel, message) Task<long> 异步发布序列化对象

💻 代码示例

完整示例,涵盖全部 5 种 Redis 数据类型以及发布/订阅:

using Snet.Redis;

var redis = RedisOperate.Instance(new RedisData.Basics
{
    ConnectStr = "127.0.0.1:6379",
    DataBaseID = 0,
    TAG = "Demo:"
});

await redis.OnAsync();

// ── 字符串 ──────────────────────────────────
await redis.StringSetAsync("key1", "value1");
Console.WriteLine(await redis.StringGetAsync("key1"));

// ── 列表 ────────────────────────────────────
await redis.ListRightPushAsync("mylist", "item1");
await redis.ListRightPushAsync("mylist", "item2");
Console.WriteLine(await redis.ListLeftPopAsync("mylist")); // "item1"

// ── 哈希 ────────────────────────────────────
await redis.HashSetAsync("user", "name", "Alice");
await redis.HashSetAsync("user", "age", "30");
Console.WriteLine(await redis.HashGetAsync("user", "name"));

// ── 有序集合 ───────────────────────────────
await redis.SortedSetAddAsync("scores", "player1", 100);
var top = await redis.SortedSetRangeByRankAsync("scores");
foreach (var item in top) Console.WriteLine(item);

// ── 键操作 ──────────────────────────
Console.WriteLine(await redis.KeyExistsAsync("key1")); // True
await redis.KeyDeleteAsync("*"); // 清除所有 Demo: 键

// ── 发布/订阅 ─────────────────────────────────
await redis.SubscribeAsync("news", (channel, value) =>
    Console.WriteLine($"收到: {value}"));
await redis.PublishAsync("news", "Breaking!");

await redis.OffAsync();

对象序列化示例:

public class Order
{
    public string Id { get; set; }
    public decimal Total { get; set; }
    public DateTime CreatedAt { get; set; }
}

// 存储
var order = new Order { Id = "ORD-001", Total = 99.99m, CreatedAt = DateTime.UtcNow };
await redis.StringSetAsync("order:001", order);

// 读取
var saved = await redis.StringGetAsync<Order>("order:001");
Console.WriteLine($"订单 {saved.Id}: ${saved.Total}");

连接事件监控:

redis.OnInfoEventHandler += (sender, args) =>
{
    switch (args.EventType)
    {
        case "ConnectionRestored":
            Console.WriteLine($"[重新连接] {args.Message}");
            break;
        case "ConnectionFailed":
            Console.WriteLine($"[连接失败] {args.Message}");
            break;
        case "ErrorMessage":
            Console.WriteLine($"[错误] {args.Message}");
            break;
    }
};

❓ FAQ

1. 如何连接远程 Redis 服务器?

ConnectStr 设置为远程端点,可选带密码:

"remote-host:6379,password=xxx"

如需 TLS/SSL 连接,追加 ,ssl=true

2. Redis 宕机时会发生什么?

所有方法返回其断开连接时的默认值(布尔值返回 false,字符串返回 null,长整型返回 -1,泛型对象返回 default)。连接事件会在恢复时触发,方便您响应重连。

3. TAG 前缀是否支持通配符删除?

支持。同步方法 KeyDelete("*") 使用 Lua KEYS 匹配所有带 TAG 前缀的键,确保仅删除属于您应用的键。

4. 操作是否线程安全?

是的。StackExchange.Redis 的 ConnectionMultiplexer 是完全线程安全的。RedisOperate 中的单例模式确保在您的应用中安全共享。

5. 如何处理对象序列化?

使用泛型 <T> 方法,写入时自动通过 JSON(Snet.Core 的 ToJson())进行序列化,读取时自动反序列化。无需手动序列化。

6. 同步和异步 API 有什么区别?

两种 API 提供相同的行为。异步方法使用基于 Task 的模式,在异步上下文(ASP.NET Core、后台服务)中应优先使用,以避免阻塞线程。同步方法适用于控制台应用和同步上下文。


📅 版本历史

日期 版本 变更
2026-07-23 当前版本,支持 .NET 8/10