redis client protocol 实现

在官网中有许多已经实现好的redis client;有需要可以参考一下。

其实前一篇redis client protocol 解析,我已经对RESP做主要的解析。

下面是解析RESP的所有函数,其中对外函数是RedisProtocol::Decode:

https://github.com/XJM2013/GameEngine/blob/master/lib/src/redis/redisprotocol.cpp

static unsigned intEndLine(const char *buf, unsigned int len);static intReadNumber(const char *buf, unsigned int len);static char_ReadString(const char *buf, unsigned int left_len, int &read_len, RedisData **data);static intReadMessage(char type, const char *buf, unsigned int len, RedisBulkData **bulk_data);static intReadInteger(const char *buf, unsigned int len, RedisBulkData **bulk_data);static intReadString(const char *buf, unsigned int len, RedisBulkData **bulk_data);static intReadArray(const char *buf, unsigned int left_len, RedisBulkData **bulk_data);/*:1\r\n前缀 + 结果 + 结束 最少也要有4个字节*/int RedisProtocol::Decode(const char *buf, unsigned int len, RedisBulkData **bulk_data){if (len < 4){return OPR_MORE_DATA;}switch (buf[0]){case '+':return ReadMessage(REPLY_TYPE_OK, buf + 1, len – 1, bulk_data);case '-':return ReadMessage(REPLY_TYPE_ERROR, buf + 1, len – 1, bulk_data);case ':':return ReadInteger(buf + 1, len – 1, bulk_data);case '$':return ReadString(buf + 1, len – 1, bulk_data);case '*':return ReadArray(buf + 1, len – 1, bulk_data);default:return OPR_DATA_INVALID;}return OPR_DATA_INVALID;}从上面的代码来看,可以看出,代码的结构相当清晰、简洁;而且这代码的功能只是对协议的处理,不包含网络模块和其它更复杂的封装等处理,,不需要做额外的辨别阅读。对于学习协议者,个人觉得还是一个不错的选择。代码中我有做一些内存池的功能,不想使用的,可以用new/delete 或者 malloc/free代替。下面我基于前一篇文章说的5个例子,分别作为测试例子:

https://github.com/XJM2013/GameEngine/blob/master/Test/testredis.h

#ifndef TEST_REDIS_H#define TEST_REDIS_H#include <string>#include "lib/include/redis/redisprotocol.h"namespace TestRedis{void ShowBulkData(RedisBulkData *data){std::list<RedisData *>::iterator itr = data->data_list.begin();for (; itr != data->data_list.end(); ++itr){printf("type = %d\n", (*itr)->type);switch ((*itr)->type){case RedisProtocol::REPLY_TYPE_INTEGER:case RedisProtocol::REPLY_TYPE_STRING_ERROR:case RedisProtocol::REPLY_TYPE_ARRAY_ERROR:printf("data = %d\n", *(int *)(*itr)->data);break;default:printf("data = %.*s\n", (*itr)->len, (*itr)->data);break;}}}void Decode(char *reply){RedisBulkData *data = NULL;if (RedisProtocol::Decode(reply, strlen(reply), &data) <= RedisProtocol::OPR_MORE_DATA){return;}ShowBulkData(data);delete data;}// 短字符串void Test1(){char *reply = "+OK\r\n";Decode(reply);}// 错误void Test2(){char *reply = "-ERR unknown command 'seet'\r\n";Decode(reply);}// 整数void Test3(){char *reply = ":11\r\n";Decode(reply);}// 长字符串void Test4(){char *reply1 = "$3\r\ncat\r\n";printf("reply1:\n");Decode(reply1);char *reply2 = "$-1\r\n";printf("reply2:\n");Decode(reply2);}// 数组void Test5(){char *reply1 = "*2\r\n$3\r\ncat\r\n$2\r\n11\r\n";printf("reply1:\n");Decode(reply1);char *reply2 = "*2\r\n$4\r\nfish\r\n$-1\r\n";printf("reply2:\n");Decode(reply2);char *reply3 = "*-1\r\n";printf("reply3:\n");Decode(reply3);}}#endif下面输出一下Test5()的结果:

协议解析总结:

1、从C/C++的角度来看,序列化保存数据,解析更快。

例如将1000条数据序列化成1条数据:

从分配空间的角度来看,1000条数据需要分配1000次空间,1条数据需要分配1次空间。

从解析的角度来看,RESP的结构是LV结构,也就是长度-值(length-value)结构,其不需要标志类型(T,type),因为它的类型都是字符串类型。而序列化实现可以是TV和TLV结构的结合。因此C/C++里面类型与长度是一一对应的,整型就是4个字节等等;对于字符串则使用TLV结构。这里大致只需要将RESP的L与序列化的T做一个对比。一个需要匹配"\r\n",并将字符串转化成数字,一个则只需要读取第一字节则可以判断数据长度。

2、set key val\r\n,当val很长,例如10k字节长,server 要匹配10k次才能匹配到\r\n,而且val中不允许出现\r\n否则会出错。

版权声明:本文为博主原创文章,未经博主允许不得转载。

有时间,我们可以去爬山,

redis client protocol 实现

相关文章:

你感兴趣的文章:

标签云: