程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> redisclientprotocol實現

redisclientprotocol實現

編輯:DB2教程

redisclientprotocol實現


 

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

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

 

static unsigned int	EndLine(const char *buf, unsigned int len);
static int			ReadNumber(const char *buf, unsigned int len);
static char			_ReadString(const char *buf, unsigned int left_len, int &read_len, RedisData **data);
static int			ReadMessage(char type, const char *buf, unsigned int len, RedisBulkData **bulk_data);
static int			ReadInteger(const char *buf, unsigned int len, RedisBulkData **bulk_data);
static int			ReadString(const char *buf, unsigned int len, RedisBulkData **bulk_data);
static int			ReadArray(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 
#include "lib/include/redis/redisprotocol.h"

namespace TestRedis
{
	void ShowBulkData(RedisBulkData *data)
	{
		
		std::list::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否則會出錯。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved