redis client protocol 實現,redisprotocol
在官網中http://redis.io/clients有許多已經實現好的redis client;有需要可以參考一下。
其實前一篇http://blog.csdn.net/yitouhan/article/details/46612925 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否則會出錯。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。