.NET redis 客户端开源组件 FreeRedis (继 CSRedisCore 之后重写)

一、什么是 FreeRedis

FreeRedis 是一款 .NET redis 客户端开源组件,以 MIT 协议开源托管于 github,目前支持 .NET 5、.NETCore 2.1+、.NETFramework 4.0+、Xamarin,有可能已经支持 AOT 编译(目前未测试,但会往这个方向走)。

FreeRedis 会严格按照 FreeSql 的开源方式,做好单元测试,兼容平台,简单易用,有问必答,有求必应的态度,为中国 .NET 开源事业做一点点贡献。

感谢大家的支持,项目还未公开就已经获得 66 星。目前项目仍在起步阶段,欢迎小伙伴参与进来,贡献测试、或代码、或建议都可以。

项目当前的状态:

  • 版本 0.0.8(目前不建议使用在生产环境)

  • 单元测试 268 个

  • 支持 集群、哨兵、主从(已通过测试)

  • 支持 连接池

  • 支持 .NET5/.NETCore 2.1+/.NET4.0+

  • 支持 Redis6.0 所有类型

  • 支持 Redis6.0 RESP3 协议

  • API 仍然与 redis-cli 命令保持一致

  • 采用最宽松的开源协议 MIT https://github.com/2881099/FreeRedis

二、项目由来

说来话长,2016 年之前本人写了一年多 nodejs 服务端应用,使用过 node-redis 组件,真心好用。在此期间有同事不停安利 .NET 可以跨平台了,劝我快回来搞 .NET,开始我是抗拒做螃蟹第一人的,不知道是哪天下午闲着蛋疼去体验了一把 .NET Core 1.0-previewXX(不记得哪个版本了)。试了一把被吸引住了,体验感受和 expressjs 像极了,再也看不见以往 webform/mvc 的缺点。

于是我准备入坑了,入坑第一件事除了 hello world,还需要做相关调研:

  • 性能 OK

  • 设计 OK

  • 发展 OK(暂时的定级)

  • 相关组件 OK(HttpClient、Redis、Ado.NET、等等基础组件)

初始调研完成之后,接下下就要抽时间选型框架了,最终从众多框架中选择了合适团队的一款:https://github.com/simplcommerce/SimplCommerce ,在这个项目原有基础之上,结合企业规范要求定制改造,大约两个月时间完成了可生产的状态。(框架不求开始尽善尽美,只求使用中不断打磨,最终走向完美)

理想丰满现实骨干,接下来的故事就是遇到生产故障了,StackExchange.Redis、HttpClient 关于这两个组件的问题,以前讲过现在就不说了(万万没想到这么大的组件使用都能出现问题)。吃螃蟹就会掉坑,掉了坑就要想办法解决,最终与 csredis 组件结缘。

以当时的情形纵观 .NET 所有 redis 客户端组件,只有 csredis 源码最易改造支持 .NETCore(水平有限见谅),csredis 2014 年停止更新,本人于 2016 年将其改造支持 .NETCore 为主,以及增加连接池管理、集群、哨兵、redis2.8 以上的命令,在公司项目生产环境使用一年半载之后开源。

  • CSredis 原源码地址:https://github.com/ctstone/csredis

  • CSRedisCore 源码地址:https://github.com/2881099/csredis

CSRedisCore 开源这么久,nuget 下载量达到 60W,收集需求若干,bug 若干(有解决了的、也有未能重现的),基于我已经对 redis 这块很熟悉,然后 redis 5.0/6.0 又新增了蛮多特性,重新写一款 bug 更少、可维护性更好的想法产生了。

经过几个月的墨迹终于走通可用了,项目最终命名:FreeRedis

感谢 Nuget 转让包的大兄弟。

三、如何使用

1、Single machine redis (单机)

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

API 仍然与 redis-cli 命令保持一致,所以如果想了解 FreeRedis 每个方法怎么使用,去百度搜索 “redis 命令”,有很多很多很多资料。don’t say so much!!!

支持 Redis6.0 支持的所有数据类型:strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, geo, streams And BloomFilter.

.NET redis 客户端开源组件 FreeRedis (继 CSRedisCore 之后重写)

如果需要连接 IPv6,连接串请使用: [fe80::b164:55b3:4b4f:7ce6%15]:6379

2、Master-Slave (读写分离)

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

这样创建的 cli,所有写入命令都会连接 127.0.0.1:6379 执行,所有读取命令只会随机连接 127.0.0.1:6380 或 127.0.0.1:6381 执行。(内部已经为每个命令做好了读写标记)

3、Redis Sentinel (哨兵高可用)

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 //是否读写分离
    );

哨兵是一个分布式系统,为 Redis 提供高可用性解决方案,当主机 master 宕机之后,会马上出现一个新的 master 可使用,确保服务的正常运作。

哨兵模式还可以设置读写分离,缓解 master 频繁读取数据的压力,缺点:有可能读到的数据不是最新,因为 redis 从 master 同步到 slave 有延时。

4、Redis Cluster (集群)

假如你有一个 Redis Cluster 集群,其中有三个主节点(7001-7003)、三个从节点(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" }
    );

 5、Subscribe (订阅)

using (cli.Subscribe("abc", ondata)) //wait .Dispose()
{
    Console.ReadKey();
}
void ondata(string channel, string data) =>
    Console.WriteLine($"{channel} -> {data}");

 6、Scripting (脚本)

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

 7、Pipeline (管道)

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]);
}
// or Async Callback
using (var pipe = cli.StartPipe())
{
var tasks = new List<Task>();
long t0 = 0;
    task.Add(pipe.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback
    pipe.SetAsync("key2", Null);
string t2 = null;
    task.Add(pipe.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback
    pipe.EndPipe();
    Task.WaitAll(tasks.ToArray()); //wait all callback
    Console.WriteLine(t0 + ", " + t2);
}

 8、Transaction (事务)

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]);
}
// or Async Callback
using (var tran = cli.Multi())
{
var tasks = new List<Task>();
long t0 = 0;
    task.Add(tran.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback
    tran.SetAsync("key2", Null);
string t2 = null;
    task.Add(tran.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback
    tran.Exec();
    Task.WaitAll(tasks.ToArray()); //wait all callback
    Console.WriteLine(t0 + ", " + t2);
}

 9、GetDatabase (切库)

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

 四、结束

目前项目仍在起步阶段,欢迎小伙伴参与进来,贡献测试、或代码、或建议都可以。

FreeRedis 使用最宽松的开源协议 MIT https://github.com/2881099/FreeRedis

 


转自:nicye
cnblogs.com/kellynic/p/13943313.html

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...