hiredis 二进制数据存储tips

hiredis 是redis官方推荐的C/C++客户端库,笔者的使用场景是对二进制数据进行读写,初次使用有些需要格外注意的地方,可以称为踩过的小坑吧。
笔者使用hiredis 0.13.3, 对redis的读写操作进行了简单封装,以hash结构为例,代码片断如下:

bool RedisHandler::HGet(const string &name, const string &key, string &val)
{
	for (int retry = 0; retry < MAX_RETRY_TIMES; ++ retry) {
		if (!connected && !Reconnect()) {
			continue;
		}

		redisReply *reply = (redisReply *) redisCommand(redis_context, "HGET %s %s", name.data(), key.data());
		if (NULL == reply) {
			log(LOG_ERROR, "redis connection exception: %s\n", redis_context->errstr);
			connected = false;
			continue;
		}

		if (reply->type != REDIS_REPLY_STRING) {
			if (reply->type == REDIS_REPLY_ERROR) {
				log(LOG_ERROR, "redis HGet exception: %s\n", reply->str);
			}
			freeReplyObject(reply);
			return false;
		}

		val = string(reply->str, reply->len);
		freeReplyObject(reply);
		return true;
	}

	return false;
}

bool RedisHandler::HSet(const string &name, const string &key, const string &val)
{
	for (int retry = 0; retry < MAX_RETRY_TIMES; ++ retry) {
		if (!connected && !Reconnect()) {
			continue;
		}

		redisReply *reply = (redisReply *) redisCommand(redis_context, "HSET %s %s %b", name.data(), key.data(), val.data(), val.size());
		if (NULL == reply) {
			log(LOG_ERROR, "redis connection exception: %s\n", redis_context->errstr);
			connected = false;
			continue;
		}

		if (reply->type != REDIS_REPLY_INTEGER) {
			if (reply->type == REDIS_REPLY_ERROR) {
				log(LOG_ERROR, "redis HSet exception: %s\n", reply->str);
			}
			freeReplyObject(reply);
			return false;
		}

		freeReplyObject(reply);
		return true;
	}

	return false;
}

对于二进制数据存取有如下两点需要注意:
1. 对于二进制(如thrift序列化后的数据)构造格式输入时,需使用”%b”, val.data(), val.size() 的形式;
2. 对于读取存储在redis内的二进制数据时, 对返回的string一定要通过string(reply->str, reply->len)构造返回,才能读取到完整的数据;

其实对于以上注意,hiredis官方文档也有明确说明,只是我们在写时没有注意或仔细查看了,原文如下:

When you need to pass binary safe strings in a command, the %b specifier can be used. Together with a pointer to the string, it requires a size_t length argument of the string:

reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);

Refer:
1 https://github.com/redis/hiredis
2 http://yaocoder.blog.51cto.com/2668309/1297031

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注