详情

首页手游攻略 FreeRedis:实践指南

FreeRedis:实践指南

佚名 2026-09-11 16:30:02

实际看FreeRedis,先要确认它的用途: FreeRedis 是 .NET40+ redis 客户端,支持集群、哨兵、主从、发布-订阅、lua、管道、事务、流、重新搜索、客户端缓存和池化。网页与浏览器自动化里,登录状态、页面变化和失败恢复往往不稳定。我会选择一个权限清楚的网页流程做端到端短测,检查会话保持、元素定位、错误恢复和工件留存。它适合需要可观测网页自动化流程的开发者;采用前仍要看维护状态和试跑结果。

FreeRedis

  • RedisClient 保持所有方法名称与redis-cli一致
  • 支持Redis集群(需要redis-server 3.2及以上版本)
  • ⛳ 支持Redis哨兵
  • 支持Redis主从
  • 支持 Redis 发布-订阅
  • 支持 Redis Lua 脚本
  • 支持管道、交易、DelayQueue、RediSearch
  • 支持 Geo 类型命令(需要 redis-server 3.2 及以上版本)
  • 支持 Streams 类型命令(需要 redis-server 5.0 及以上版本)
  • ⚡ 支持客户端缓存(需要redis-server 6.0及以上版本)
  • 支持 Redis 6 RESP3 协议

QQ 群:4336577(已满)、8578575(可用)52508226(可用)

快速启动

public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13");
cli.Serialize = obj => JsonConvert.SerializeObject(obj);
cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type);
cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log

cli.Set("key1", "value1");
cli.MSet("key1", "value1", "key2", "value2");

string value1 = cli.Get("key1");
string[] vals = cli.MGet("key1", "key2");

支持字符串、散列、列表、集合、排序集、位图、hyperloglogs、geo、流和 BloomFilter。

参数 默认 解释一下
协议 RESP2 如果使用RESP3,需要redis 6.0环境
用户 <empty> Redis服务器用户名,需要redis-server 6.0
密码 <empty> Redis服务器密码
defaultDatabase 0 Redis服务器数据库
最大池大小 100 连接最大池大小
最小池大小 5 连接池最小大小
idleTimeout 20000 连接池中元素的空闲时间(MS),适合连接远程redis服务器
connectTimeout 10000 连接超时(MS)
receiveTimeout 10000 接收超时(MS)
sendTimeout 10000 发送超时(MS)
编码 UTF-8 字符串字符集
重试 0 协议错误重试执行次数
安全套接字层 假的 启用加密传输
姓名 <empty> 连接名称,使用client list命令查看
前缀 <empty> 键的前缀,所有方法都会有这个前缀。 cli.Set(前缀+“密钥”,111);
exitAutoDisposePool 真实 AppDomain.CurrentDomain.ProcessExit/Console.CancelKeyPress 自动处置
subscribeReadbytes 假的 订阅读取字节数

IPv6: [fe80::b164:55b3:4b4f:7ce6%15]:6379

//FreeRedis.DistributedCache
//services.AddSingleton<IDistributedCache>(new FreeRedis.DistributedCache(cli));

主从

public static RedisClient cli = new RedisClient(
    "127.0.0.1:6379,password=123,defaultDatabase=13",
    "127.0.0.1:6380,password=123,defaultDatabase=13",
    "127.0.0.1:6381,password=123,defaultDatabase=13"
    );

var value = cli.Get("key1");

在127.0.0.1:6379写入数据;从端口6380或6381随机读取数据。

⛳ Redis 哨兵

public static RedisClient cli = new RedisClient(
    "mymaster,password=123", 
    new [] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" },
    true //This variable indicates whether to use the read-write separation mode.
    );

Redis 集群

假设一个Redis集群有3个主节点(7001-7003)和3个从节点(7004-7006),则使用以下代码连接集群:

public static RedisClient cli = new RedisClient(
    new ConnectionStringBuilder[] { "192.168.0.2:7001", "192.168.0.2:7002", "192.168.0.2:7003" }
    );

⚡ 客户端缓存

需要redis-server 6.0及以上版本

cli.UseClientSideCaching(new ClientSideCachingOptions
{
    //Client cache capacity
    Capacity = 3,
    //Filtering rules, which specify which keys can be cached locally
    KeyFilter = key => key.StartsWith("Interceptor"),
    //Check long-term unused cache
    CheckExpired = (key, dt) => DateTime.Now.Subtract(dt) > TimeSpan.FromSeconds(2)
});

订阅

using (cli.Subscribe("abc", ondata)) //wait .Dispose()
{
    Console.ReadKey();
}

void ondata(string channel, string data) =>
    Console.WriteLine($"{channel} -> {data}");

xadd + xreadgroup:

using (cli.SubscribeStream("stream_key", ondata)) //wait .Dispose()
{
    Console.ReadKey();
}

void ondata(Dictionary<string, string> streamValue) =>
    Console.WriteLine(JsonConvert.SerializeObject(streamValue));

// NoAck xpending
cli.XPending("stream_key", "FreeRedis__group", "-", "+", 10);

lpush + blpop:

using (cli.SubscribeList("list_key", ondata)) //wait .Dispose()
{
    Console.ReadKey();
}

void ondata(string listValue) =>
    Console.WriteLine(listValue);

脚本编写

var r1 = cli.Eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 
    new[] { "key1", "key2" }, "first", "second") as object[];

var r2 = cli.Eval("return {1,2,{3,'Hello World!'}}") as object[];

cli.Eval("return redis.call('set',KEYS[1],'bar')", 
    new[] { Guid.NewGuid().ToString() })

管道

using (var pipe = cli.StartPipe())
{
    pipe.IncrBy("key1", 10);
    pipe.Set("key2", Null);
    pipe.Get("key1");

    object[] ret = pipe.EndPipe();
    Console.WriteLine(ret[0] + ", " + ret[2]);
}

交易

using (var tran = cli.Multi())
{
    tran.IncrBy("key1", 10);
    tran.Set("key2", Null);
    tran.Get("key1");

    object[] ret = tran.Exec();
    Console.WriteLine(ret[0] + ", " + ret[2]);
}

GetDatabase:切换数据库

using (var db = cli.GetDatabase(10))
{
    db.Set("key1", 10);
    var val1 = db.Get("key1");
}

扫描

支持集群模式

foreach (var keys in cli.Scan("*", 10, null))
{
    Console.WriteLine(string.Join(", ", keys));
}

DelayQueue

var delayQueue = cli.DelayQueue("TestDelayQueue");

//Add queue
delayQueue.Enqueue($"Execute in 5 seconds.", TimeSpan.FromSeconds(5));
delayQueue.Enqueue($"Execute in 10 seconds.", DateTime.Now.AddSeconds(10));
delayQueue.Enqueue($"Execute in 15 seconds.", DateTime.Now.AddSeconds(15));
delayQueue.Enqueue($"Execute in 20 seconds.", TimeSpan.FromSeconds(20));
delayQueue.Enqueue($"Execute in 25 seconds.", DateTime.Now.AddSeconds(25));
delayQueue.Enqueue($"Execute in 2024-07-02 14:30:15", DateTime.Parse("2024-07-02 14:30:15"));

//Consumption queue
await delayQueue.DequeueAsync(s =>
{
    output.WriteLine($"{DateTime.Now}:{s}");

    return Task.CompletedTask;
});

RediSearch

cli.FtCreate(...).Execute();
cli.FtSearch(...).Execute();
cli.FtAggregate(...).Execute();
//... or ...

[FtDocument("index_post", Prefix = "blog:post:")]
class TestDoc
{
    [FtKey]
    public int Id { get; set; }

    [FtTextField("title", Weight = 5.0)]
    public string Title { get; set; }

    [FtTextField("category")]
    public string Category { get; set; }

    [FtTextField("content", Weight = 1.0, NoIndex = true)]
    public string Content { get; set; }

    [FtTagField("tags")]
    public string[] Tags { get; set; } //or string

    [FtNumericField("views")]
    public int Views { get; set; }
}

var repo = cli.FtDocumentRepository<TestDoc>();
repo.CreateIndex();

repo.Save(new TestDoc { Id = 1, Title = "test title1 word", Category = "class 1", Content = "test content 1 suffix", Tags = "user1,user2", Views = 101 });
repo.Save(new TestDoc { Id = 2, Title = "prefix test title2", Category = "class 2", Content = "test infix content 2", Tags = "user2,user3", Views = 201 });
repo.Save(new TestDoc { Id = 3, Title = "test title3 word", Category = "class 1", Content = "test word content 3", Tags = "user2,user5", Views = 301 });

repo.Delete(1, 2, 3);

repo.Save(new[]
{
    new TestDoc { Id = 1, Title = "test title1 word", Category = "class 1", Content = "test content 1 suffix", Tags = "user1,user2", Views = 101 },
    new TestDoc { Id = 2, Title = "prefix test title2", Category = "class 2", Content = "test infix content 2", Tags = "user2,user3", Views = 201 },
    new TestDoc { Id = 3, Title = "test title3 word", Category = "class 1", Content = "test word content 3", Tags = "user2,user5", Views = 301 }
});

var list = repo.Search("*").InFields(a => new { a.Title }).ToList();
list = repo.Search("*").Return(a => new { a.Title, a.Tags }).ToList();
list = repo.Search("*").Return(a => new { tit1 = a.Title, tgs1 = a.Tags, a.Title, a.Tags }).ToList();

list = repo.Search(a => a.Title == "word" && a.Tags.Contains("user1")).Filter(a => a.Views, 1, 1000).ToList();
list = repo.Search("word").ToList();
list = repo.Search("@title:word").ToList();
点击查看更多
推荐专题
热门阅读