.net core实现aop_redis实时计算

.net core实现aop_redis实时计算引言  最近工作上有需要使用redis,于是便心血来潮打算自己写一个C#客户端。经过几天的努力,目前该客户端已经基本成型,下面简单介绍一下。通信协议  要想自行实现redisClient,则必须先要了解Redis的socket能信协议。新版统一请求协议在Redis1.2版本中引入,并最终在Redis2.0版本成为Redis服务器通…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

引言

  最近工作上有需要使用redis,于是便心血来潮打算自己写一个C#客户端。经过几天的努力,目前该客户端已经基本成型,下面简单介绍一下。

通信协议

  要想自行实现redisClient,则必须先要了解Redis的socket能信协议。新版统一请求协议在 Redis 1.2 版本中引入, 并最终在 Redis 2.0 版本成为 Redis 服务器通信的标准方式。在这个协议中, 所有发送至 Redis 服务器的参数都是二进制安全(binary safe)的。

  以下是这个协议的一般形式:

*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF

  注:命令本身也作为协议的其中一个参数来发送。举个例子, 以下是一个命令协议的打印版本:

1 *3
2 $3
3 SET
4 $5
5 mykey
6 $7
7 myvalue

  这个命令的实际协议值如下:

1 "*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

  稍后看到, 这种格式除了用作命令请求协议之外, 也用在命令的回复协议中: 这种只有一个参数的回复格式被称为批量回复(Bulk Reply)。统一协议请求原本是用在回复协议中, 用于将列表的多个项返回给客户端的, 这种回复格式被称为多条批量回复(Multi Bulk Reply)。一个多条批量回复以 *<argc>\r\n 为前缀, 后跟多条不同的批量回复, 其中 argc 为这些批量回复的数量。

  Redis 命令会返回多种不同类型的回复。一个状态回复(或者单行回复,single line reply)是一段以 “+” 开始、 “\r\n” 结尾的单行字符串。通过检查服务器发回数据的第一个字节, 可以确定这个回复是什么类型:

  • 状态回复(status reply)的第一个字节是 “+”
  • 错误回复(error reply)的第一个字节是 “-”
  • 整数回复(integer reply)的第一个字节是 “:”
  • 批量回复(bulk reply)的第一个字节是 “$”
  • 多条批量回复(multi bulk reply)的第一个字节是 “*”

.net Core Socket

  说起socket,就不得不说IOCP了,这个方案本身就是为了解决多连接、高并发而设计的;但是话又说回来,任何方案都有局限性,不可能解决所有问题;这里不去讨论用在这里是否合适,反正本人就是想这么试一把:用一个简单的ioc模式实现SAEA.Socket,并为此设定各种场景,反过来优化SAEA.Socket本身。下面是一段服务器接收连接的代码:

 1         private void ProcessAccept(SocketAsyncEventArgs args)
 2         {
 3             if (args == null)
 4             {
 5                 args = new SocketAsyncEventArgs();
 6                 args.Completed += ProcessAccepted;
 7             }
 8             else
 9             {
10                 args.AcceptSocket = null;
11             }
12             if (!_listener.AcceptAsync(args))
13             {
14                 ProcessAccepted(_listener, args);
15             }
16         }

 项目结构

  在网上找到redis的命令文档后,本人觉的准备工作差不多了,可以初步定一下项目结构:

  Core:定义的是redisclient相关最基本的业务

  Interface:定义的是一些需要抽象出来的接口

  Model:定义的是redis的数据模型及其请求、回复的类型枚举

  Net:这里就是将继承实现SAEA.Socket而来的RedisConnection通信基础

命令解码器

  通过前面的准备工作了解到redisClient的关键在于命令的编解码,至于高大上算法或redis官方算法的实现,本人没有去详细了解,一冲动就自行实现了自定义版的解码器。

 1         public string Coder(RequestType commandName, params string[] @params)
 2         {
 3             _autoResetEvent.WaitOne();
 4             _commandName = commandName;
 5             var sb = new StringBuilder();
 6             sb.AppendLine("*" + @params.Length);
 7             foreach (var param in @params)
 8             {
 9                 sb.AppendLine("$" + param.Length);
10                 sb.AppendLine(param);
11             }
12             return sb.ToString();
13         }

 1 public ResponseData Decoder()  2  {  3 var result = new ResponseData();  4  5 string command = null;  6  7 string error = null;  8  9 var len = 0;  10  11 switch (_commandName)  12  {  13 case RequestType.PING:  14 command = BlockDequeue();  15 if (GetStatus(command, out error))  16  {  17 result.Type = ResponseType.OK;  18 result.Data = "PONG";  19  }  20 else  21  {  22 result.Type = ResponseType.Error;  23 result.Data = error;  24  }  25 break;  26 case RequestType.AUTH:  27 case RequestType.SELECT:  28 case RequestType.SLAVEOF:  29 case RequestType.SET:  30 case RequestType.DEL:  31 case RequestType.HSET:  32 case RequestType.HDEL:  33 case RequestType.LSET:  34 command = BlockDequeue();  35 if (GetStatus(command, out error))  36  {  37 result.Type = ResponseType.OK;  38 result.Data = "OK";  39  }  40 else  41  {  42 result.Type = ResponseType.Error;  43 result.Data = error;  44  }  45 break;  46 case RequestType.TYPE:  47 command = BlockDequeue();  48 if (GetStatusString(command, out string msg))  49  {  50 result.Type = ResponseType.OK;  51  }  52 else  53  {  54 result.Type = ResponseType.Error;  55  }  56 result.Data = msg;  57 break;  58 case RequestType.GET:  59 case RequestType.GETSET:  60 case RequestType.HGET:  61 case RequestType.LPOP:  62 case RequestType.RPOP:  63 case RequestType.SRANDMEMBER:  64 case RequestType.SPOP:  65 len = GetWordsNum(BlockDequeue(), out error);  66 if (len == -1)  67  {  68 result.Type = ResponseType.Empty;  69 result.Data = error;  70  }  71 else  72  {  73 result.Type = ResponseType.String;  74 result.Data += BlockDequeue();  75  }  76 break;  77 case RequestType.KEYS:  78 case RequestType.HKEYS:  79 case RequestType.LRANGE:  80 case RequestType.SMEMBERS:  81 result.Type = ResponseType.Lines;  82 var sb = new StringBuilder();  83 var rn = GetRowNum(BlockDequeue(), out error);  84 if (!string.IsNullOrEmpty(error))  85  {  86 result.Type = ResponseType.Error;  87 result.Data = error;  88 break;  89  }  90 //再尝试读取一次,发现有回车行出现  91 if (rn == -1) rn = GetRowNum(BlockDequeue(), out error);  92 if (!string.IsNullOrEmpty(error))  93  {  94 result.Type = ResponseType.Error;  95 result.Data = error;  96 break;  97  }  98 if (rn > 0)  99  { 100 for (int i = 0; i < rn; i++) 101  { 102 len = GetWordsNum(BlockDequeue(), out error); 103  sb.AppendLine(BlockDequeue()); 104  } 105  } 106 result.Data = sb.ToString(); 107 break; 108 case RequestType.HGETALL: 109 case RequestType.ZRANGE: 110 case RequestType.ZREVRANGE: 111 result.Type = ResponseType.KeyValues; 112 sb = new StringBuilder(); 113 rn = GetRowNum(BlockDequeue(), out error); 114 if (!string.IsNullOrEmpty(error)) 115  { 116 result.Type = ResponseType.Error; 117 result.Data = error; 118 break; 119  } 120 if (rn > 0) 121  { 122 for (int i = 0; i < rn; i++) 123  { 124 len = GetWordsNum(BlockDequeue(), out error); 125  sb.AppendLine(BlockDequeue()); 126  } 127  } 128 result.Data = sb.ToString(); 129 break; 130 case RequestType.DBSIZE: 131 case RequestType.EXISTS: 132 case RequestType.EXPIRE: 133 case RequestType.PERSIST: 134 case RequestType.SETNX: 135 case RequestType.HEXISTS: 136 case RequestType.HLEN: 137 case RequestType.LLEN: 138 case RequestType.LPUSH: 139 case RequestType.RPUSH: 140 case RequestType.LREM: 141 case RequestType.SADD: 142 case RequestType.SCARD: 143 case RequestType.SISMEMBER: 144 case RequestType.SREM: 145 case RequestType.ZADD: 146 case RequestType.ZCARD: 147 case RequestType.ZCOUNT: 148 case RequestType.ZREM: 149 case RequestType.PUBLISH: 150 var val = GetValue(BlockDequeue(), out error); 151 if (!string.IsNullOrEmpty(error)) 152  { 153 result.Type = ResponseType.Error; 154 result.Data = error; 155 break; 156  } 157 if (val == 0) 158  { 159 result.Type = ResponseType.Empty; 160  } 161 else 162  { 163 result.Type = ResponseType.OK; 164  } 165 result.Data = val.ToString(); 166 break; 167 case RequestType.INFO: 168 var rnum = GetWordsNum(BlockDequeue(), out error); 169 if (!string.IsNullOrEmpty(error)) 170  { 171 result.Type = ResponseType.Error; 172 result.Data = error; 173 break; 174  } 175 var info = ""; 176 while (info.Length < rnum) 177  { 178 info += BlockDequeue(); 179  } 180 result.Type = ResponseType.String; 181 result.Data = info; 182 break; 183 case RequestType.SUBSCRIBE: 184 var r = ""; 185 while (IsSubed) 186  { 187 r = BlockDequeue(); 188 if (r == "message\r\n") 189  { 190 result.Type = ResponseType.Sub; 191  BlockDequeue(); 192 result.Data = BlockDequeue(); 193  BlockDequeue(); 194 result.Data += BlockDequeue(); 195 break; 196  } 197  } 198 break; 199 case RequestType.UNSUBSCRIBE: 200 var rNum = GetRowNum(BlockDequeue(), out error); 201 var wNum = GetWordsNum(BlockDequeue(), out error); 202  BlockDequeue(); 203 wNum = GetWordsNum(BlockDequeue(), out error); 204 var channel = BlockDequeue(); 205 var vNum = GetValue(BlockDequeue(), out error); 206 IsSubed = false; 207 break; 208  } 209  _autoResetEvent.Set(); 210 return result; 211 }

命令的封装与测试

  有了socket、redisCoder之后,现在就可以按照官方的redis命令来进行.net core的封装了。本人将这些操作封装到RedisClient、RedisDataBase两个类中,然后又想到连接复用的问题,简单实现了一个连接池RedisClientFactory的类。这样一来就可以好好的来实验一把,看看之前的设想最终能不能实现了:

.net core实现aop_redis实时计算
.net core实现aop_redis实时计算

 1 /****************************************************************************  2 *Copyright (c) 2018 Microsoft All Rights Reserved.  3 *CLR版本: 4.0.30319.42000  4 *机器名称:WENLI-PC  5 *公司名称:Microsoft  6 *命名空间:SAEA.RedisSocketTest  7 *文件名: Program  8 *版本号: V1.0.0.0  9 *唯一标识:3d4f939c-3fb9-40e9-a0e0-c7ec773539ae  10 *当前的用户域:WENLI-PC  11 *创建人: yswenli  12 *电子邮箱:wenguoli_520@qq.com  13 *创建时间:2018/3/17 10:37:15  14 *描述:  15 *  16 *=====================================================================  17 *修改标记  18 *修改时间:2018/3/19 10:37:15  19 *修改人: yswenli  20 *版本号: V1.0.0.0  21 *描述:  22 *  23 *****************************************************************************/  24 using SAEA.Commom;  25 using SAEA.RedisSocket;  26 using System;  27  28 namespace SAEA.RedisSocketTest  29 {  30 class Program  31  {  32 static void Main(string[] args)  33  {  34 ConsoleHelper.Title = "SAEA.RedisSocketTest";  35 ConsoleHelper.WriteLine("输入ip:port连接RedisServer");  36  37 var ipPort = ConsoleHelper.ReadLine();  38 if (string.IsNullOrEmpty(ipPort))  39  {  40 ipPort = "127.0.0.1:6379";  41  }  42 RedisClient redisClient = new RedisClient(ipPort);  43  redisClient.Connect();  44 //redisClient.Connect("wenli");   45  46  47 var info = redisClient.Info();  48 if (info.Contains("NOAUTH Authentication required."))  49  {  50 while (true)  51  {  52 ConsoleHelper.WriteLine("请输入redis连接密码");  53 var auth = ConsoleHelper.ReadLine();  54 if (string.IsNullOrEmpty(auth))  55  {  56 auth = "yswenli";  57  }  58 var a = redisClient.Auth(auth);  59 if (a.Contains("OK"))  60  {  61 break;  62  }  63 else  64  {  65  ConsoleHelper.WriteLine(a);  66  }  67  }  68  }  69  70 //redisConnection.SlaveOf();  71  72 //redisConnection.Ping();  73  74 redisClient.Select(1);  75  76 //ConsoleHelper.WriteLine(redisConnection.Type("key0"));  77  78 ConsoleHelper.WriteLine("dbSize:{0}", redisClient.DBSize().ToString());  79  80  81 RedisOperationTest(redisClient, true);  82  ConsoleHelper.ReadLine();  83  }  84  85 private static void RedisOperationTest(object sender, bool status)  86  {  87 RedisClient redisClient = (RedisClient)sender;  88 if (status)  89  {  90 ConsoleHelper.WriteLine("连接redis服务器成功!");  91  92 #region key value  93  94 ConsoleHelper.WriteLine("回车开始kv插值操作...");  95  ConsoleHelper.ReadLine();  96 for (int i = 0; i < 1000; i++)  97  {  98 redisClient.GetDataBase().Set("key" + i, "val" + i);  99  } 100 //redisConnection.GetDataBase().Exists("key0"); 101 ConsoleHelper.WriteLine("kv插入完成..."); 102 103 ConsoleHelper.WriteLine("回车开始获取kv值操作..."); 104  ConsoleHelper.ReadLine(); 105 106 var keys = redisClient.GetDataBase().Keys().Data.ToArray(false, "\r\n"); 107 108 foreach (var key in keys) 109  { 110 var val = redisClient.GetDataBase().Get(key); 111 ConsoleHelper.WriteLine("Get val:" + val); 112  } 113 ConsoleHelper.WriteLine("获取kv值完成..."); 114 115 ConsoleHelper.WriteLine("回车开始开始kv移除操作..."); 116  ConsoleHelper.ReadLine(); 117 foreach (var key in keys) 118  { 119  redisClient.GetDataBase().Del(key); 120  } 121 ConsoleHelper.WriteLine("移除kv值完成..."); 122 #endregion 123 124 125 #region hashset 126 string hid = "wenli"; 127 128 ConsoleHelper.WriteLine("回车开始HashSet插值操作..."); 129  ConsoleHelper.ReadLine(); 130 for (int i = 0; i < 1000; i++) 131  { 132 redisClient.GetDataBase().HSet(hid, "key" + i, "val" + i); 133  } 134 ConsoleHelper.WriteLine("HashSet插值完成..."); 135 136 ConsoleHelper.WriteLine("回车开始HashSet插值操作..."); 137  ConsoleHelper.ReadLine(); 138 var hkeys = redisClient.GetDataBase().GetHKeys(hid).Data.ToArray(); 139 foreach (var hkey in hkeys) 140  { 141 var val = redisClient.GetDataBase().HGet(hid, hkey); 142 ConsoleHelper.WriteLine("HGet val:" + val.Data); 143  } 144 145 var hall = redisClient.GetDataBase().HGetAll("wenli"); 146 ConsoleHelper.WriteLine("HashSet查询完成..."); 147 148 ConsoleHelper.WriteLine("回车开始HashSet移除操作..."); 149  ConsoleHelper.ReadLine(); 150 foreach (var hkey in hkeys) 151  { 152  redisClient.GetDataBase().HDel(hid, hkey); 153  } 154 ConsoleHelper.WriteLine("HashSet移除完成..."); 155 156 157 #endregion 158 159 160 //redisConnection.GetDataBase().Suscribe((c, m) => 161 //{ 162 // ConsoleHelper.WriteLine("channel:{0} msg:{1}", c, m); 163 // redisConnection.GetDataBase().UNSUBSCRIBE(c); 164 //}, "c39654"); 165 166 167 ConsoleHelper.WriteLine("测试完成!"); 168  } 169 else 170  { 171 ConsoleHelper.WriteLine("连接失败!"); 172  } 173  } 174  } 175 }

View Code

  经过上面的代码测试,使用redis-cli工具进行monitor命令监控发现——搞定了!另外源码本人已发到github上面了,SAEA.RedisSocket的详细可查看:https://github.com/yswenli/SAEA/tree/master/Src/SAEA.RedisSocket

 

 

转载请标明本文来源:http://www.cnblogs.com/yswenli/p/8608661.html 
更多内容欢迎star作者的github:https://github.com/yswenli/SAEA
如果发现本文有什么问题和任何建议,也随时欢迎交流~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/182727.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • java打jar包的几种方式详解

    java打jar包的几种方式详解

    2021年11月22日
  • mt4历史数据回测_mt410年历史数据

    mt4历史数据回测_mt410年历史数据这个网站只能下载2001年-当前时间前一个月的数据,还是挺全的。但是下载下来之后好像是一分钟图的,妈蛋其实我想要1小时图的EURUSD历史数据。网站地址:http://www.fxfupan.com/datacenter.html它们网站上的复盘大师可以试下,回去我就试下看看他们的软件怎么样刚才找到一个更好的,上面的东西可以不必看了。福汇官方有个历史数据下载器软件(初阶免费),登录自己的福汇账号,…

  • ideavim怎么用_idea常用快捷键图文

    ideavim怎么用_idea常用快捷键图文学了这么久的VIM,当然还是要用在开发上。下面来介绍一下ideavim这个插件。IdeaVim是用于基于IntelliJ平台的IDE的Vim仿真插件。IdeaVim支持许多Vim功能,包括普通/插入/可视模式,动作键,删除/更改,标记,寄存器,一些Ex命令,Vimregexp,通过〜/.ideavimrc配置,宏,窗口命令等。另外还可以自定义ideavim的快捷键,定制专属的快捷键。安装I…

  • 一个对话让你明白架构师是做什么的?[通俗易懂]

    一个对话让你明白架构师是做什么的?[通俗易懂]阅读本文大概需要6分钟。很多人都想知道架构师是做什么?我们看看下面的一段对话。菜鸟——刚入门的程序员老鸟——资深架构师老鸟:菜鸟,你的目标是什么?菜鸟:我要成为一个软件架构师。老鸟:对一个年轻的工程师来说,这是一个很好的目标。那你为什么要成为架构师呢?菜鸟:我要领导一个团队,还要做所有关于数据库、框架和Web服务器的重要决定。老鸟…

    2022年10月27日
  • vue 加载页面时触发时间_Vue 刷新页面时会触发事件吗「建议收藏」

    vue 加载页面时触发时间_Vue 刷新页面时会触发事件吗「建议收藏」使用localstorage做本地存储,然后我想在刷新页面或者离开页面的调用localstorage方法1、页面刷新使用localstorage,也就是当vue被实例化之后有如下几个可以供你使用:exportdefault{beforecreate(){//创建前状态}created(){//创建完毕状态}beforeMount(){//挂载前状态}mounted(){//…

  • Android系统五大布局详解Layout

    Android系统五大布局详解Layout我们知道Android系统应用程序一般是由多个Activity组成,而这些Activity以视图的形式展现在我们面前,视图都是由一个一个的组件构成的。组件就是我们常见的Button、TextEdit等等。那么我们平时看到的Android手机中那些漂亮的界面是怎么显示出来的呢?这就要用到Android的布局管理器了,网上有人比喻的很好:布局好比是建筑里的框架,组件按照布局的要求依次排列,就组成了用于

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号