最近项目使用中要改造redis客户端,看了下文档,总结分享一下。

阅读目录:

  1. 协议规范
  2. 基础通信
  3. 状态命令
  4. set、get命令
  5. 管道、事务
  6. 总结

协议规范

redis允许客户端以TCP方式连接,默认6379端口。传输数据都以\r\n结尾。

请求格式

*<number of arguments>\r\n$<number of bytes of argument 1>\r\n<argument data>\r\n

例:*1\r\n$4\r\nINFO\r\n

响应格式

1:简单字符串,非二进制安全字符串,一般是状态回复。  +开头,例:+OK\r\n

2: 错误信息。          -开头, 例:-ERR unknown command 'mush'\r\n

3: 整型数字。                            :开头, 例::1\r\n

4:大块回复值,最大512M。           $开头+数据长度。 例:$4\r\mush\r\n

5:多条回复。                           *开头, 例:*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n

基础通信

定义配置类:

  1. public class Configuration
  2. {
  3. public string Host { get; set; }
  4. public int Port { get; set; }
  5. /// <summary>
  6. /// Socket 是否正在使用 Nagle 算法。
  7. /// </summary>
  8. public bool NoDelaySocket { get; set; }
  9.  
  10. public Configuration()
  11. {
  12. Host = "localhost";
  13. Port = 6379;
  14. NoDelaySocket = false;
  15. }
  16. }

实现socket连接:

  1. public class RedisBaseClient
  2. {
  3. //配置文件
  4. private Configuration configuration;
  5. //通信socket
  6. private Socket socket;
  7. //接收字节数组
  8. private byte[] ReceiveBuffer = new byte[];
  9.  
  10. public RedisBaseClient(Configuration config)
  11. {
  12. configuration = config;
  13. }
  14.  
  15. public RedisBaseClient()
  16. : this(new Configuration())
  17. {
  18. }
  19.  
  20. public void Connect()
  21. {
  22. if (socket != null && socket.Connected)
  23. return;
  24. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
  25. {
  26. NoDelay = configuration.NoDelaySocket
  27. };
  28. socket.Connect(configuration.Host, configuration.Port);
  29. if (socket.Connected)
  30. return;
  31. Close();
  32. }
  33.  
  34. /// <summary>
  35. /// 关闭client
  36. /// </summary>
  37. public void Close()
  38. {
  39. socket.Disconnect(false);
  40. socket.Close();
  41. }
  42. }

调用:

  1. RedisBaseClient redis = new RedisBaseClient();
  2. redis.Connect();

服务端成功响应:

  

状态命令

定义Redis命令枚举:

  1. public enum RedisCommand
  2. {
  3. GET, //获取一个key的值
  4. INFO, //Redis信息。
  5. SET, //添加一个值
  6. EXPIRE, //设置过期时间
  7. MULTI, //标记一个事务块开始
  8. EXEC, //执行所有 MULTI 之后发的命令
  9. }

发送命令构建:

  1. public string SendCommand(RedisCommand command, params string[] args)
  2. {
  3. //请求头部格式, *<number of arguments>\r\n
  4. const string headstr = "*{0}\r\n";
  5. //参数信息 $<number of bytes of argument N>\r\n<argument data>\r\n
  6. const string bulkstr = "${0}\r\n{1}\r\n";
  7.  
  8. var sb = new StringBuilder();
  9. sb.AppendFormat(headstr, args.Length + );
  10.  
  11. var cmd = command.ToString();
  12. sb.AppendFormat(bulkstr, cmd.Length, cmd);
  13.  
  14. foreach (var arg in args)
  15. {
  16. sb.AppendFormat(bulkstr, arg.Length, arg);
  17. }
  18. byte[] c = Encoding.UTF8.GetBytes(sb.ToString());
  19. try
  20. {
  21. Connect();
  22. socket.Send(c);
  23.  
  24. socket.Receive(ReceiveBuffer);
  25. Close();
  26. return ReadData();
  27. }
  28. catch (SocketException e)
  29. {
  30. Close();
  31. }
  32. return null;
  33. }
  34. private string ReadData()
  35. {
  36. var data = Encoding.UTF8.GetString(ReceiveBuffer);
  37. char c = data[];
  38. //错误消息检查。
  39. if (c == '-') //异常处理。
  40. throw new Exception(data);
  41. //状态回复。
  42. if (c == '+')
  43. return data;
  44. return data;
  45. }

调用:

  1. private void button1_Click(object sender, EventArgs e)
  2. {
  3. RedisBaseClient redis = new RedisBaseClient();
  4. var result = redis.SendCommand(RedisCommand.INFO);
  5. richTextBox1.Text = result;
  6. }

输出响应,其$937是数据包的长度。

set、get命令

调用:

  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3. RedisBaseClient redis = new RedisBaseClient();
  4. var result = redis.SendCommand(RedisCommand.SET, "msg", "testvalue");
  5. richTextBox1.Text = result.ToString();
  6. }
  7. private void button3_Click(object sender, EventArgs e)
  8. {
  9. RedisBaseClient redis = new RedisBaseClient();
  10. var result = redis.SendCommand(RedisCommand.GET, "msg");
  11. richTextBox1.Text = result.ToString();
  12. }

输出

管道、事务

二者都是走的MULTI,EXEC命令,原子操作。管道就是发送命令(无需等上次命令回复),进入命令队列,然后多条命令一次执行,并返回客户端结果。

平常使用ServiceStack.Redis客户端都直接set了,其实是set、expire 2个命令。 简单实现如下:

  1. public void CreatePipeline()
  2. {
  3. SendCommand(RedisCommand.MULTI, new string[] {}, true);
  4. }
  5. public string EnqueueCommand(RedisCommand command, params string[] args)
  6. {
  7. return SendCommand(command, args, true);
  8. }
  9. public string FlushPipeline()
  10. {
  11. var result = SendCommand(RedisCommand.EXEC, new string[] {}, true);
  12. Close();
  13. return result;
  14. }
  15. public string SendCommand(RedisCommand command, string[] args, bool isPipeline=false)
  16. {
  17. //请求头部格式, *<number of arguments>\r\n
  18. const string headstr = "*{0}\r\n";
  19. //参数信息 $<number of bytes of argument N>\r\n<argument data>\r\n
  20. const string bulkstr = "${0}\r\n{1}\r\n";
  21.  
  22. var sb = new StringBuilder();
  23. sb.AppendFormat(headstr, args.Length + );
  24.  
  25. var cmd = command.ToString();
  26. sb.AppendFormat(bulkstr, cmd.Length, cmd);
  27.  
  28. foreach (var arg in args)
  29. {
  30. sb.AppendFormat(bulkstr, arg.Length, arg);
  31. }
  32. byte[] c = Encoding.UTF8.GetBytes(sb.ToString());
  33. try
  34. {
  35. Connect();
  36. socket.Send(c);
  37.  
  38. socket.Receive(ReceiveBuffer);
  39. if (!isPipeline)
  40. {
  41. Close();
  42. }
  43. return ReadData();
  44. }
  45. catch (SocketException e)
  46. {
  47. Close();
  48. }
  49. return null;
  50. }
  51. public string SetByPipeline(string key, string value, int second)
  52. {
  53. this.CreatePipeline();
  54. this.EnqueueCommand(RedisCommand.SET, key, value);
  55. this.EnqueueCommand(RedisCommand.EXPIRE, key, second.ToString());
  56. return this.FlushPipeline();
  57. }

调用:

  1. private void button4_Click(object sender, EventArgs e)
  2. {
  3. RedisBaseClient redis = new RedisBaseClient();
  4. richTextBox1.Text = redis.SetByPipeline("cnblogs", "mushroom", );
  5. }

输出:

*2 表示2条回复。

+2 表示命令执行OK。

:1  表示命令执行的结果

总结

本文只是简单的实现,有兴趣的同学,可以继续下去。

客户端实现这块,Socket连接池管理相较复杂些。

参考资源:

http://redis.io/topics/protocol

https://github.com/ServiceStack/ServiceStack.Redis

c#实现redis客户端(一)的更多相关文章

  1. StackExchange.Redis客户端读写主从配置,以及哨兵配置。

    今天简单分享一下StackExchange.Redis客户端中配置主从分离以及哨兵的配置. 关于哨兵如果有不了解的朋友,可以看我之前的一篇分享,当然主从复制文章也可以找到.http://www.cnb ...

  2. 使用StackExchange.Redis客户端进行Redis访问出现的Timeout异常排查

    问题产生 这两天业务系统在redis的使用过程中,当并行客户端数量达到200+之后,产生了大量timeout异常,典型的异常信息如下: Timeout performing HVALS Parser2 ...

  3. Redis客户端之Spring整合Jedis,ShardedJedisPool集群配置

    Jedis设计 Jedis作为推荐的java语言redis客户端,其抽象封装为三部分: 对象池设计:Pool,JedisPool,GenericObjectPool,BasePoolableObjec ...

  4. 从零开始写redis客户端(deerlet-redis-client)之路——第一个纠结很久的问题,restore引发的血案

    引言 正如之前的一篇博文,LZ最近正在从零开始写一个redis的客户端,主要目的是为了更加深入的了解redis,当然了,LZ也希望deerlet客户端有一天能有一席之地.在写的过程当中,LZ遇到了一个 ...

  5. Redis 客户端配置及示例

    一.redis自定义配置节点 <configSections> <section name ="RedisConfig" type="Amy.Toolk ...

  6. Redis客户端Java服务接口封装

    最近在学习Redis并集成到Spring中去,发现Spring的RedisTemplate并不好用,还没有MongoTemplate好用. 而且发现Jedis和ShardedJedis的方法非常多,覆 ...

  7. "Redis客户端连接数一直降不下来"的有关问题解决

    [线上问题] "Redis客户端连接数一直降不下来"的问题解决 前段时间,上线了新的 Redis缓存(Cache)服务,准备替换掉 Memcached. 为什么要将 Memcach ...

  8. spring整合redis客户端及缓存接口设计(转)

    一.写在前面 缓存作为系统性能优化的一大杀手锏,几乎在每个系统或多或少的用到缓存.有的使用本地内存作为缓存,有的使用本地硬盘作为缓存,有的使用缓存服务器.但是无论使用哪种缓存,接口中的方法都是差不多. ...

  9. 全球领先的redis客户端:SFedis

    零.背景 这个客户端起源于我们一个系统的生产问题. 一.问题的发生 在我们的生产环境上发生了两次redis服务端连接数达到上限(我们配置的单节点连接数上限为8000)导致无法创建连接的情况.由于这个系 ...

随机推荐

  1. WiX Toolset 教程索引页

    注意:虽然WiX Toolset功能强大,但其学习曲线相对较高.请慎重选择: 若没有足够时间.没心思搞的请绕行至inno setup.installshield.nisi.setupfactory.. ...

  2. OpenGL帧缓存对象(FBO:Frame Buffer Object)(转载)

    原文地址http://www.songho.ca/opengl/gl_fbo.html 但有改动. OpenGL Frame BufferObject(FBO) Overview: 在OpenGL渲染 ...

  3. linux线程

    线程:轻量级进程,在资源.数据方面不需要进行复制 不间断地跟踪指令执行的路径被称为执行路线 进程的结构:task_struck:地址空间 线程:轻量级的进程 在同一个进程中创建的线程,在共享进程的地址 ...

  4. webform文件上传、图片水印、验证码

    文件上传: 所用控件:FileUpload 使用时的思路: 1.判断用户是否选中了文件 FileUpload.FileName获取选中的文件名,判断长度,如果长度大于零就代表已经选择了文件 JS端:通 ...

  5. linux中redis安装

    一.登录redis官网下载redis-3.0.7.tar.gz 二.通过ftp工具上传至自己的服务器中 三.tar -zxvf redis-3.0.7.tar.gz解压 四.cd redis-3.0. ...

  6. Kafka、RabbitMQ、RocketMQ消息中间件的对比 —— 消息发送性能-转自阿里中间件

    引言 分布式系统中,我们广泛运用消息中间件进行系统间的数据交换,便于异步解耦.现在开源的消息中间件有很多,前段时间我们自家的产品 RocketMQ (MetaQ的内核) 也顺利开源,得到大家的关注. ...

  7. [原创] Go语言在Centos上的部署

    序言 Golang是个好东西啊.部署非常简单,对于运维人员来说太爽了. 传统的Nginx啊Apache啊,外加PHP以及各个插件啊搞得头晕. 用了Go之后就什么都不需要了.只要把生成好的文件向服务器上 ...

  8. 【Centos】修改网卡名字&随之出现的问题

    自从学了工具tcpdump之后,里面会需要涉及到针对某个网卡抓包,因而会输入网卡名字,可是centOS7蛋疼的网卡默认命名实在是让人心碎,所以就想到了要修改网卡名字,步骤如下:(以下步骤涉及到我的错误 ...

  9. JavaScript高级程序设计-(1)html中使用JavaScript

    html中使用JavaScript 1.延迟脚本 script标签定义了defer属性,脚本会被延迟到整个页面都解析完毕后运行 详细内容如下: 2.异步脚本 script标签定义了async属性,as ...

  10. 《DSP using MATLAB》示例Example5.22

    代码: Nmax = 2048; fft_time = zeros(1, Nmax); for n = 1:1:Nmax x=rand(1,n); t=clock; fft(x); fft_time( ...