NServiceKit.Redis这个名字可能听说过的不多,但是在.NET平台使用redis的人一般都接触过ServiceStack.Redis,ServiceStack.Redis从v4开始收费了,NServiceKit.Redis是这个程序的免费开源版本,其源码与ServiceStack.Redis v3基本相同,但是它们好像都不支持SCAN命令(也许是我没发现),尝试用执行Lua Script的ExecLuaAsList等方法也不能返回两个值,所以只好修改了源代码实现这个功能。
SCAN介绍
SCAN cursor [MATCH pattern] [COUNT count]
支持三个参数:
cursor ?类似于数据库查询中的游标,表示开始扫描的位置
MATACH 表示key的匹配模式,全部就是*
COUNT 表示要扫描的数目,返回的数据条数
SCAN执行后每次都返回一个新的cursor,然后用cursor执行下一次SCAN,直到cursor等于0,表示完整迭代完毕。
迭代过程示例:
NServiceKit.Redis修改
在NServiceKit.Redis内部其实已经支持返回多个值,RedisClient中的Slowlog就是一个例子,其内部数据结构是一个嵌套数组,方便组装多级多种不同的返回值。
打开NServiceKit.Redis项目中的RedisNativeClient_Utils.cs文件,找到方法:SendExpectDeeplyNestedMultiData:
protected object[] SendExpectDeeplyNestedMultiData(params byte[][] cmdWithBinaryArgs) { // 发送要执行的SCAN命令 if (!SendCommand(cmdWithBinaryArgs)) throw CreateConnectionError(); if (Pipeline != null) { throw new NotSupportedException("Pipeline is not supported."); } // 读取返回的嵌套数据 return ReadDeeplyNestedMultiData(); }
其中又调用了ReadDeeplyNestedMultiData:
private object[] ReadDeeplyNestedMultiData() { return (object[])ReadDeeplyNestedMultiDataItem(); }
其中又调用了ReadDeeplyNestedMultiDataItem,这是个关键的方法:
private object ReadDeeplyNestedMultiDataItem() { int c = SafeReadByte(); // 读取数据类型 if (c == -1) throw CreateResponseError("No more data"); var s = ReadLine(); Log("R: {0}", s); // 根据不同的数据类型处理不同 switch (c) { case '$': // $开头后跟数据的字节长度,按照这个长度读取数据 return ParseSingleLine(string.Concat(char.ToString((char)c), s)); case '-': throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); case '*': // *开头后跟数据条数,从下一行开始是相关数据 int count; if (int.TryParse(s, out count)) { var array = new object[count]; for (int i = 0; i < count; i++) { // 如果数据有多级,通过递归方式读取出来 array[i] = ReadDeeplyNestedMultiDataItem(); } return array; } break; default: return s; } throw CreateResponseError("Unknown reply on multi-request: " + c + s); }
通过上边这个方法SCAN命令的返回值就全取到了,下一步就要利用这个方法来实现SCAN命令。
1、修改NServiceKit.Interfaces项目中的IRedisClient接口,增加一个ScanKeys方法。
string[] ScanKeys(ref long cursor, string pattern = null, long? count = null);
三个参数对应SCAN命令的三个参数,cursor是ref的,可以返回下一步迭代的cursor。
返回值是查询到的key数组。
2、修改NServiceKit.Redis项目中的RedisNativeClient类,增加一个Scan方法。
public object[] Scan(long cursor, string pattern = null, long? count = null) { if (pattern == null) pattern = "*"; if (count == null) count = 10; return SendExpectDeeplyNestedMultiData(Commands.Scan, cursor.ToUtf8Bytes(), "MATCH".ToUtf8Bytes(), pattern.ToUtf8Bytes(), "COUNT".ToUtf8Bytes(), count.Value.ToUtf8Bytes()); }
这是一个底层方法,对返回值不做加工,其中调用了前边提到的SendExpectDeeplyNestedMultiData方法。
NServiceKit.Redis中本没有Scan命令,这里还在Commands类中增加了一个字段Scan:
public readonly static byte[] Scan = "SCAN".ToUtf8Bytes();
3、修改NServiceKit.Redis项目中的RedisClient类,增加一个ScanKeys方法,实现IRedisClient接口中新增的ScanKeys:
public string[] ScanKeys(ref long cursor, string pattern = null, long? count = null) { var result = base.Scan(cursor, pattern, count); if (result.Length <= 0) { cursor = 0; return null; } // 返回的游标 cursor = long.Parse(Encoding.UTF8.GetString((byte[])result[0])); // 返回的Keys if (result.Length > 1) { var items = (object[])result[1]; string[] keys = new string[items.Length]; for (int i = 0; i < items.Length; i++) { keys[i] = Encoding.UTF8.GetString((byte[])items[i]); } return keys; } return null; }
到此NServiceKit.Redis就支持SCAN命令了。
有兴趣的可以改一下试试,或者直接下载我编译的dll:NServiceKit.Redis。
关键字: NServiceKit.Redis Redis SCAN
发表评论
相关文章
国内AI资源汇总,AI聊天、AI绘画、AI写作、AI视频、AI设计、AI编程、AI音乐等,国内顺畅访问,无需科学上网。
扫码或点击进入:萤火AI大全
文章分类
最新评论