C++ Redis mset 二进制数据接口封装方案

需求

C++中使用hiredis客户端接口访问redis;
需要使用mset一次设置多个二进制数据

以下给出三种封装实现方案;

简单拼接方案

在redis-cli中,mset的语法是这样的:

/opt/colin$./redis-cli mset a 11 b 22 c 333
OK

按照这样的语法拼接后,直接使用hiredis字符串接口redisCommand传递:

void msetNotBinary(redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal )
{
if(vtKey.size() != vtVal.size())
{
throw runtime_error( "Redis error" );
} string strCmd = "MSET";
for(int i = 0; i < vtKey.size(); i++)
{
strCmd += " "+vtKey[i]+" "+vtVal[i];
}
cout << "strCmd:" << strCmd << endl; void * r = redisCommand(c, strCmd.c_str() );
if ( !r )
throw runtime_error( "Redis error" );
freeReplyObject( r );
} void do_test( redisContext *c )
{
vector<string> vtKey;
vector<string> vtVal; vtKey.push_back("A");
vtVal.push_back("AAAA");
vtKey.push_back("B");
vtVal.push_back("BBBB");
vtKey.push_back("C");
vtVal.push_back("CCCC");
//add a binary data
vtKey.push_back("D");
vtVal.push_back("");
char a[] = "ABCDE";
a[2] = 0;
vtVal[3].assign(a,5); try
{
msetNotBinary(c, vtKey, vtVal );
//mset1( c, vtKey, vtVal );
//mset2( c, vtKey, vtVal );
}
catch ( runtime_error & )
{
cout << "Error" << endl;
}
} int main(int argc, char *argv[])
{
redisContext *c; c = redisConnect("127.0.0.1",6379);
if (c->err)
{
cout << "Connection error: " << c->errstr << endl;
return -1;
} do_test(c); redisFree(c); return 0;
}

这种方式可以处理mset多个字符串数据,但对于数据内容为二进制数据的无能为力;

redisCommandArgv接口传递 方案

对于多个参数传递,hiredis提供了以下接口,这个接口中最后一个参数是所有的传入数据的内容长度,
就是说这个接口是二进制安全的:

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

主要工作就是构造一个动态的二维数组char ** argv,其中涉及到char **const char **的转换,有一定的风险,
关于这一点前一篇文章已经谈到;

void mset1( redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal )
{
if(vtKey.size() != vtVal.size())
{
throw runtime_error( "Redis error" );
} char ** argv = new char*[vtKey.size() + vtVal.size() + 1 ];
size_t * argvlen = new size_t[vtKey.size() + vtVal.size() + 1 ]; int j = 0;
argv[j] = new char[5];
memcpy(argv[j],"MSET",4);
argvlen[j] = 4;
++j; for(int i = 0 ; i < vtKey.size();i++)
{
argvlen[j] = vtKey[i].length();
argv[j] = new char[argvlen[j]];
memset((void*)argv[j],0,argvlen[j] );
memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length());
j++; argvlen[j] = vtVal[i].length();
argv[j] = new char[argvlen[j]];
memset((void*)argv[j],0,argvlen[j]);
memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length());
j++;
} //if not use const_cast<const char**> ,compile error
//for why assign from char** to const char** error, see my blog ...
void *r = redisCommandArgv(c, vtKey.size() + vtVal.size() + 1, const_cast<const char**>(argv), argvlen );
if ( !r )
throw runtime_error( "Redis error" );
freeReplyObject( r ); for(int i = 0;i < vtKey.size();i++)
{
delete [] argv[i];
argv[i] = NULL;
} delete []argv;
delete []argvlen;
argv = NULL;
}

redisCommandArgv接口传递的Vector方案

还是使用redisCommandArgv接口,使用vector来构造这个const char **,这个方法是从参考资料1中学到的:

void mset2( redisContext *c, const vector<string> &vtKey, const vector<string> & vtVal)
{
if(vtKey.size() != vtVal.size())
{
throw runtime_error( "Redis error" );
} vector<const char *> argv( vtKey.size() + vtVal.size() + 1 );
vector<size_t> argvlen( vtKey.size() + vtVal.size() + 1 );
int j = 0; static char msetcmd[] = "MSET";
argv[j] = msetcmd;
argvlen[j] = sizeof(msetcmd)-1;
++j; for(int i = 0;i< vtKey.size();++i)
{
argvlen[j] = vtKey[i].length();
argv[j] = new char[argvlen[j]];
memset((void*)argv[j],0,argvlen[j] );
memcpy((void*)argv[j],vtKey[i].data(),vtKey[i].length());
j++; argvlen[j] = vtVal[i].length();
argv[j] = new char[argvlen[j]];
memset((void*)argv[j],0,argvlen[j]);
memcpy((void*)argv[j],vtVal[i].data(),vtVal[i].length());
j++;
} void *r = redisCommandArgv(c, argv.size(), &(argv[0]), &(argvlen[0]) );
  
  for(int i = 0; i < argv.size();i++)
  {
    delete argv[i];
    argv[i] = NULL;
  }
if ( !r )
throw runtime_error( "Redis error" );
freeReplyObject( r );
}

这样,就实现二进制数据的传递;

二进制校验

程序执行后,可以用redis-cli来验证:

对于非二进制安全的实现,二进制内容是截断的:
/opt/app/colin$./redis-cli get D
"AB"
而二进制安全的实现接口,二进制数据的0通过转义方式显示:
/opt/app/colin$./redis-cli get D
"AB\x00DE"

完整可执行的代码详见github:https://github.com/me115/cppset/tree/master/2DimArray

参考资料

https://gist.github.com/dspezia/1455082

Posted by: 大CC | 8JAN,2015
博客:blog.me115.com [订阅]

微博:新浪微博

C++ Redis mset 二进制数据接口封装方案的更多相关文章

  1. Redis客户端Java服务接口封装

    最近在学习Redis并集成到Spring中去,发现Spring的RedisTemplate并不好用,还没有MongoTemplate好用. 而且发现Jedis和ShardedJedis的方法非常多,覆 ...

  2. Sword redis存取二进制数据

    #include "hiredis/hiredis.h" /* redis头文件 */ #include <stdio.h> #include <stdlib.h ...

  3. redis集群数据迁移

    redis集群数据备份迁移方案 n  迁移环境描述及分析 当前我们面临的数据迁移环境是:集群->集群. 源集群: 源集群为6节点,3主3备 主 备 192.168.112.33:8001 192 ...

  4. StackExchange.Redis帮助类解决方案RedisRepository封装(字符串类型数据操作)

    本文版权归博客园和作者本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/tag/NoSql/ 目录 一.基础配置封装 二.String字符串类型数据操作封 ...

  5. Memcache,Redis,MongoDB(数据缓存系统)方案对比与分析

    mongodb和memcached不是一个范畴内的东西.mongodb是文档型的非关系型数据库,其优势在于查询功能比较强大,能存储海量数据.mongodb和memcached不存在谁替换谁的问题. 和 ...

  6. redis 数据备份持久化方案

    本文链接:http://www.cnblogs.com/zhenghongxin/p/9050219.html 使用两种备份方案 备份方案选择RDB和AOF同时进行备份,必须打开AOF的持久化机制,除 ...

  7. Redis 数据持久化的方案的实现

    原文:Redis 数据持久化的方案的实现 版权声明:m_nanle_xiaobudiu https://blog.csdn.net/m_nanle_xiaobudiu/article/details/ ...

  8. 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。

    在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...

  9. JAVAEE——宜立方商城06:Redis安装、数据类型和持久化方案、Redis集群分析与搭建、实现缓存和同步

    1. 学习计划 1.首页轮播图展示 2.Redis服务器搭建 3.向业务逻辑中添加缓存. 4.使用redis做缓存 5.缓存同步. 2. 首页轮播图动态展示 2.1. 功能分析 根据分类id查询内容列 ...

随机推荐

  1. unix

    BSD (Berkeley Software Distribution,伯克利软件套件)是Unix的衍生系统,在1977至1995年间由加州大学伯克利分校开发和发布的.历史上, BSD曾经被认为是UN ...

  2. AC68U 内Linux 终端前后的切换,终端挂起和恢复

    ssh 登录终端后, 如果想切换到本地上来, 可以按: -,Ctrl+Z 如果要恢复到远程端,则命令: fg

  3. linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】

    本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一. ...

  4. 【python cookbook】【字符串与文本】9.将Unicode文本统一表示为规范形式

    问题:确保所有的Unicode字符串都拥有相同的底层 解决方案:为解决同一个文本拥有多种不同的表示形式问题,应该先将文本统一表示为规范形式,这可以通过unicodedata模块来完成, unicode ...

  5. xhr dojo load

    require(["dojo/_base/xhr"], function(xhr) { // Execute a HTTP GET request xhr.get({ // The ...

  6. Google Map: JavaScript API RefererNotAllowedMapError

    visite https://console.developers.google.com/apis/credentials and select the project you use. http:/ ...

  7. Linux workqueue疑问【转】

    转自:http://blog.csdn.net/angle_birds/article/details/9387365 各位大神,你们好.我在使用workqueue的过程中遇到一个问题. 项目采用uC ...

  8. Redis整合Spring结合使用缓存实例

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的 ...

  9. 修改ECSHOP后台的商品列表里显示该商品品牌

    如何在在ECSHOP后台的商品列表中也显示商品的品牌”.下面就来最模板讲一下如何来修改.此方法只保证在ECSHOP2.7.2版本下有效,其他版本请参照修改. 第一步:首先我们来打开程序文件: /adm ...

  10. The C++ Standard Library --- A Tutorial Reference 读书笔记

    5.2 Smart Pointer(智能指针) shared_ptr的aliasing构造函数,接受一个shared pointer和一个raw pointer.它允许你掌握一个事实:某对象拥有另一个 ...