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. win7 chm 打开失败记录

    近期学习rails,制作的html帮助文件想生成chm文件,用了几个网上的html制作chm软件,生成的chm无法打开. 网上大部分解决方法是修改文件属性,Unlock之后可解决. 以前遇到过打不开, ...

  2. 有趣的insert死锁

    昨天看到一个很有意思的死锁,拿来记录下: 环境:deadlock on 事务隔离级别: read commited 表结构: root::>show create table lingluo\G ...

  3. pybot/robot命令参数说明

    Robot Framework -- A generic test automation framework Version: 3.0 (Python 3.4.0 on win32) Usage: r ...

  4. ch2-4:遇到嵌套列表进行缩进打印

    1.增加一个参数来控制缩进打印:level '''这是一个模块,可以打印列表,其中可能包含嵌套列表''' def print_list(the_list,level): ""&qu ...

  5. n阶乘 尾数0的个数

    class Solution {public: int trailingZeroes(int n) {            if(n<=0) return 0; int i=0;        ...

  6. HTTP 请求未经客户端身份验证方案“Anonymous”授权。从服务器收到的身份验证标头为“Negotiate,NTLM”

    转自:http://www.cnblogs.com/geqinggao/p/3270499.html 近来项目需要Web Service验证授权,一般有两种解决方案: 1.通过通过SOAP Heade ...

  7. PHP文件系统处理相关操作

    <?php/* PHP文件系统处理 * 所有文件处理都是使用系统函数完成的. * 是基于Linux/Unix系统为模型 * * 文件系统处理的作用: * 1. 所有的项目离不开文件处理 * 2. ...

  8. URL List

    wifi driver http://wenku.baidu.com/view/5fb275e9b8f67c1cfad6b85e.html http://wenku.baidu.com/view/a5 ...

  9. Java中的ClassLoader

      Java中类的加载过程(如Dog类):   通过类型信息定位Dog.class文件. 载入Dog.class文件,创建相应的Class对象. 执行父类的静态字段定义时初始化语句和父类的静态初始化块 ...

  10. A Simple Problem with Integers(树状数组HDU4267)

    A Simple Problem with Integers Time Limit: 5000/1500 MS (Java/Others) Memory Limit: 32768/32768 K (J ...