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]) );
    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 [订阅]

微博:新浪微博

时间: 2024-08-03 07:21:06

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

安装redis-py并连接Redis服务器设置和获取redis的二进制数据

本文档简单介绍一下使用python版的Redis客户端redis-py来连接Redis并执行设置和获取redis的二进制数据. 说明: set,get,setnx,append等命令同样也可以用于设置二进制数据. 因为Redis的自带的客户端redis-cli不方便设置二进制数据,所以我们这里使用Python的客户端来进行 安装redis-py有三种方式: 1.   pip install redis 2.   easy_install redis 3.   从源码安装: python setu

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

Memcache,Redis,MongoDB(数据缓存系统)方案对比与分析 数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求. 二.解决方案: 1.通过高速服务器Cache缓存数据库数据 2.内存数据库 (这里仅从数据缓存方面考虑,当然,后期可以采用Hadoop+HBase+Hive等分布式存储分析平台) 三.主流解Cache和数据库对比: 上述技术基本上代表了当今在数据存储方面所有的实现方案,其中主要涉及到了普通关系型数据库(MySQL/PostgreSQL),NoSQL数据

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

mongodb和memcached不是一个范畴内的东西.mongodb是文档型的非关系型数据库,其优势在于查询功能比较强大,能存储海量数据.mongodb和memcached不存在谁替换谁的问题. 和memcached更为接近的是redis.它们都是内存型数据库,数据保存在内存中,通过tcp直接存取,优势是速度快,并发高,缺点是数据类型有限,查询功能不强,一般用作缓存.在我们团队的项目中,一开始用的是memcached,后来用redis替代. 相比memcached: 1.redis具有持久化机

StackExchange.Redis帮助类解决方案RedisRepository封装(基础配置)

本文版权归博客园和作者吴双本人共同所有,转载和爬虫,请注明原文地址.http://www.cnblogs.com/tdws/p/5815735.html 写在前面 这不是教程,分享而已,也欢迎园友们多提建议和指正.关于更多详细介绍,请到github上看Docs,下面附上地址. 关于Redis基础控制它台操作有疑问的,欢迎阅读本人Redis系列命令拾遗分享 http://www.cnblogs.com/tdws/tag/NoSql/ 如今StackService.Redis已经转向商业版本.4.0

Flume+Kafka+Storm+Redis构建大数据实时处理系统:实时统计网站PV、UV+展示

[TOC] 1 大数据处理的常用方法 前面在我的另一篇文章中<大数据采集.清洗.处理:使用MapReduce进行离线数据分析完整案例>中已经有提及到,这里依然给出下面的图示: 前面给出的那篇文章是基于MapReduce的离线数据分析案例,其通过对网站产生的用户访问日志进行处理并分析出该网站在某天的PV.UV等数据,对应上面的图示,其走的就是离线处理的数据处理方式,而这里即将要介绍的是另外一条路线的数据处理方式,即基于Storm的在线处理,在下面给出的完整案例中,我们将会完成下面的几项工作: 1

swoole+Redis实现实时数据推送

<?php /** * *************************************** * 单进程保护 * * *************************************** */ $phpSelf = realpath($_SERVER['PHP_SELF']); $lockFile = $phpSelf.'.lock'; $lockFileHandle = fopen($lockFile, "w"); if ($lockFileHandle =

BLOB存储图片文件二进制数据是非对错

子在一天一天虚度,生活也在一天一天中茫然 做人做事哪能尽如人意,付出多少收获多少虽然存在偏颇,但是不劳而获的心态是万万不对的,更不能去怨天尤人,低调为人.做好自己就可以了 改进你的系统的最好的方法是先避免做“蠢事”.我并不是说你或你开发的东西“蠢”,只是有些决定很容易被人们忽略掉其暗含的牵连,认识不到这样做对 系统维护尤其是系统升级带来多大的麻烦.作为一个顾问,像这样的事情我到处都能见到,我还从来没有见过做出这样的决定的人有过好的结果的. 图片,文件,二进制数据 既然数据库支持 BLOB 类型的

在nginx中使用lua直接访问mysql和memcaced达到数据接口的统一

安装nginx参见<nginx+lua+redis构建高并发应用> 让nginx 中的nginx_lua_module支持mysql 和memcache 下载 https://github.com/agentzh/lua-resty-memcached https://github.com/agentzh/lua-resty-mysql 对于访问接口的统一有很多的处理方式,这里介绍使用nginx lua 访问mysql并用memcache缓存起来. 配置如下: ... location /ge

JavaScript二进制数据序列化和反序列化

最近业余时间在搞h5小游戏,由于同步协议过于频繁,和服务器之间的同步直接用json就显得太浪费了,于是我们商讨之下决定改用二进制.学习过程中并没有遇到一篇就解决问题的文章,遂再总结一发. 1.二进制数据的存储 ArrayBuffer对象.TypedArray对象.DataView对象是JavaScript操作二进制数据的一个接口. (1)ArrayBuffer对象:代表内存之中的一段二进制数据,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指