设为首页 收藏本站
查看: 1732|回复: 0

[经验分享] c#实现redis客户端(一)

[复制链接]

尚未签到

发表于 2015-7-19 14:23:36 | 显示全部楼层 |阅读模式
  最近项目使用中要改造redis客户端。就看了下文档,总结分享一下。

目录
  一:协议规范
  二:基础通信
  三:状态命令
  四:set、get命令
  五:管道、事务
  六:总结

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

请求格式
  *\r\n$\r\n\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

二:基础通信
  定义配置类:



public class Configuration
{
public string Host { get; set; }
public int Port { get; set; }
///
/// Socket 是否正在使用 Nagle 算法。
///
public bool NoDelaySocket { get; set; }
public Configuration()
{
Host = "localhost";
Port = 6379;
NoDelaySocket = false;
}
}

  实现socket连接:



public class RedisBaseClient
{
//配置文件
private Configuration configuration;
//通信socket
private Socket socket;
//接收字节数组
private byte[] ReceiveBuffer = new byte[100000];
public RedisBaseClient(Configuration config)
{
configuration = config;
}
public RedisBaseClient()
: this(new Configuration())
{
}
public void Connect()
{
if (socket != null && socket.Connected)
return;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
NoDelay = configuration.NoDelaySocket
};
socket.Connect(configuration.Host, configuration.Port);
if (socket.Connected)
return;
Close();
}
///
/// 关闭client
///
public void Close()
{
socket.Disconnect(false);
socket.Close();
}
}
  调用:



RedisBaseClient redis = new RedisBaseClient();
redis.Connect();
  服务端成功响应:
DSC0000.png   

三:状态命令
  定义Redis命令枚举:



public enum RedisCommand
{
GET, //获取一个key的值
INFO, //Redis信息。  
SET, //添加一个值
EXPIRE, //设置过期时间
MULTI, //标记一个事务块开始
EXEC, //执行所有 MULTI 之后发的命令
}
  发送命令构建:



  public string SendCommand(RedisCommand command, params string[] args)
{
//请求头部格式, *\r\n
const string headstr = "*{0}\r\n";
//参数信息       $\r\n\r\n
const string bulkstr = "${0}\r\n{1}\r\n";
var sb = new StringBuilder();
sb.AppendFormat(headstr, args.Length + 1);
var cmd = command.ToString();
sb.AppendFormat(bulkstr, cmd.Length, cmd);
foreach (var arg in args)
{
sb.AppendFormat(bulkstr, arg.Length, arg);
}
byte[] c = Encoding.UTF8.GetBytes(sb.ToString());
try
{
Connect();
socket.Send(c);
socket.Receive(ReceiveBuffer);
Close();
return ReadData();
}
catch (SocketException e)
{
Close();
}
return null;
}
private string ReadData()
{
var data = Encoding.UTF8.GetString(ReceiveBuffer);
char c = data[0];
//错误消息检查。
if (c == '-') //异常处理。
throw new Exception(data);
//状态回复。
if (c == '+')
return data;
return data;
}
  调用:



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

  响应输出。  937是数据长度。
DSC0001.png

四:set、get命令
  调用:



   private void button2_Click(object sender, EventArgs e)
{
RedisBaseClient redis = new RedisBaseClient();
var result = redis.SendCommand(RedisCommand.SET, "msg", "testvalue");
richTextBox1.Text = result.ToString();
}
private void button3_Click(object sender, EventArgs e)
{
RedisBaseClient redis = new RedisBaseClient();
var result = redis.SendCommand(RedisCommand.GET, "msg");
richTextBox1.Text = result.ToString();
}
  输出
DSC0002.png DSC0003.png

五:管道、事务
  二者都是走的MULTI,EXEC命令,原子操作。管道就是发送命令(无需等上次命令回复),进入命令队列,然后多条命令一次执行,并返回客户端结果。
  我们平常使用ServiceStack.Redis客户端都直接set了,其实是set、expire 2个命令。 简单实现如下:



        public void CreatePipeline()
{
SendCommand(RedisCommand.MULTI, new string[] {}, true);
}
public string EnqueueCommand(RedisCommand command, params string[] args)
{
return SendCommand(command, args, true);
}
public string FlushPipeline()
{
var result = SendCommand(RedisCommand.EXEC, new string[] {}, true);
Close();
return result;
}
public string SendCommand(RedisCommand command, string[] args, bool isPipeline=false)
{
//请求头部格式, *\r\n
const string headstr = "*{0}\r\n";
//参数信息       $\r\n\r\n
const string bulkstr = "${0}\r\n{1}\r\n";
var sb = new StringBuilder();
sb.AppendFormat(headstr, args.Length + 1);
var cmd = command.ToString();
sb.AppendFormat(bulkstr, cmd.Length, cmd);
foreach (var arg in args)
{
sb.AppendFormat(bulkstr, arg.Length, arg);
}
byte[] c = Encoding.UTF8.GetBytes(sb.ToString());
try
{
Connect();
socket.Send(c);
socket.Receive(ReceiveBuffer);
if (!isPipeline)
{
Close();
}
return ReadData();
}
catch (SocketException e)
{
Close();
}
return null;
}
public string SetByPipeline(string key, string value, int second)
{
this.CreatePipeline();
this.EnqueueCommand(RedisCommand.SET, key, value);
this.EnqueueCommand(RedisCommand.EXPIRE, key, second.ToString());
return this.FlushPipeline();
}     

  调用:



  private void button4_Click(object sender, EventArgs e)
{
RedisBaseClient redis = new RedisBaseClient();
richTextBox1.Text = redis.SetByPipeline("cnblogs", "mushroom", 1000);
}
  
  输出:
DSC0004.png 2条回复。
  

六:总结
  本文只是简单的实现。有兴趣的同学,可以继续下去。 ps:有点重复造轮子的感觉。
  客户端实现这块,Socket连接池管理较复杂些。

参考资源
  1:http://redis.io/topics/protocol
  2:https://github.com/ServiceStack/ServiceStack.Redis

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88342-1-1.html 上篇帖子: redis在.net架构中的应用(2)--并发和原子操作不可兼得 下篇帖子: 初识Redis及Redis在Windows下的安装和使用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表