redis client protocol 实现

在官网中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 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 <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否则会出错。

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

时间: 2025-01-12 11:50:02

redis client protocol 实现的相关文章

redis client protocol 解析

在官网http://redis.io/topics/protocol有对redis通信协议有做说明. 基于下面的一些原因,我想解析redis client protocol: 1.足够了解通信协议,有助于做出更好的系统设计. 2.学习RESP的设计思想,不仅能扩展我的思维,也许将来能应用于我的代码中. 3.因为有些人想将redis client直接并入自己已有的系统中:包括我在内.这个将在我下一篇文章再做说明. 下面我翻译一下http://redis.io/topics/protocol一些我认

【轮子狂魔】手把手教你自造Redis Client

为什么做Redis Client? Redis Client顾名思义,redis的客户端,主要是封装了一些对于Redis的操作. 而目前用的比较广泛的 ServiceStack.Redis 不学好,居然开始收费了. 作为轮子狂魔,是可忍孰不可忍啊.于是我决定自己造轮子了. Redis通信协议 先给个Redis官方的通信协议地址:http://redisdoc.com/topic/protocol.html 关键是我截图的部分,我们可以得到以下几个信息: 1.tcp协议 2.默认端口6379 3.

Redis client Python usage

mport redis r_server = redis.Redis('localhost') #this line creates a new Redis object and #connects to our redis server r_server.set('test_key', 'test_value') #with the created redis object we can #submits redis commands as its methods print 'previou

lettuce--Advanced Redis client

redis官方提供的java client: git地址:https://github.com/mp911de/lettuceAdvanced Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.http://redis.paluch.biz Introduction Lettuce is a scalable thread

StackExchange.Redis Client

StackExchange.Redis Client 这期我们来看StackExchange.Redis,这是redis 的.net客户端之一.Redis是一个开源的内存数据存储,可以用来做数据库,缓存或者消息代理服务.目前有不少人在使用ServiceStack.Redis这个.net客户端,但是这个的最新版本目前已经变成了商业软件.对于ServiceStack.Redis这种行为,我们没有什么好说的,留给我们的选择是使用低版本的开源版本或者转向其他的客户端. 要说到StackExchange.

redis client 2.0.0 pipeline 的list的rpop bug

描写叙述: redis client 2.0.0 pipeline 的list的rpop 存在严重bug,rpop list的时候,假设list已经为空的时候,rpop出来的Response依旧不为null,导致吊response.get()方法抛异常 代码: @Test public void testRedisPipeline(){ Jedis jedis = null; try{ jedis = new Jedis("127.0.0.1",6379); Pipeline pipe

./redis-trib.rb 报错:/usr/local/rvm/gems/ruby-2.4.2/gems/redis-4.0.1/lib/redis/client.rb:119:in `call&#39;: ERR Slot 0 is already busy (Redis::CommandError)

错误提示是 slot插槽被占用了(这是 搭建集群前时,以前redis的旧数据和配置信息没有清理干净.) 解决方案是 用redis-cli 登录到每个节点执行  flushall  和 cluster reset  就可以了. 然后重新执行群集脚本命令: ./redis-trib.rb create --replicas 1 192.168.*.*:7001 192.168.*.*:7002 192.168.*.*:7003 192.168.*.*:7004 192.168.*.*:7005  1

Intro to Jedis – the Java Redis Client Library

转自:http://www.baeldung.com/jedis-java-redis-client-library 1. Overview This article is an introduction to Jedis, a client library in Java for Redis – the popular in-memory data structure store that can persist on disk as well. It is driven by a keyst

redis client API

想知道redis针对各种编程语言推荐的接口API实现,请参考http://redis.io/clients/ 选择python语言,则使用https://github.com/andymccurdy/redis-py Installation redis-py requires a running Redis server. See Redis's quickstart(http://redis.io/topics/quickstart) for installation instruction