Hiredis是redis数据库一个轻量的C语言客户端库。git
之因此轻量是因为它只是简单的提供了对redis操做语句支持的接口,并无实现具体的操做语句的功能。但正是因为这种设计使咱们只要熟悉了通用的redis操做语句就能够很容易的使用该库和redis数据库进行交互。github
除了支持发送命令和接收应答/应答数据,它提供了对应答数据的解析操做。并且这个基于I/O层的数据流解析操做设计考虑到了复用性,能够对应答数据进行通用的解析操做。redis
Hirides仅仅支持二进制安全的redis协议,因此你只能针对版本号大于等于1.2.0的redis服务端使用。数据库
库包含多种API,包括同步命令操做API、异步命令操做API和对应答数据进行解析的API。数组
版本0.9.0是hiredis不少特性一次大的更新。可是对现有代码进行升级应该不会形成大的问题。升级时,要记住的关键一点是大于等于0.9.0的版本是使用redisContext*来保持链接状态,以前的版本只是使用了无状态的文件描述符。安全
有几个API须要介绍异步
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);socket
函数 redisConnect 被用来建立一个 redisContext。这个 context 是hiredis持有的链接状态。redisConnect 结构体有一个整型的 err 变量来标识链接错误码,若是链接错误则为非零值。变量 errstr 标识链接结果的文字描述。更多这方面的信息会在如下Errors章节说明。当你使用 redisConnect 来建立链接时应该检查err变量来判断是否链接正常创建。ide
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c != NULL && c->err) {
printf("Error: %s\n", c->errstr); // handle error }函数
有多种方法能够发送命令到redis。
首先介绍的是redisCommand。此函数相似于printf的使用方式,如
reply = redisCommand(context, "SET foo bar");
相似于printf的s%格式化方式,如
reply = redisCommand(context, "SET foo %s", value);
当你须要发送二进制安全的命令能够采用%b的格式化方式,同时须要一个字符串指针和size_t类型的字符串长度参数,以下
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
在API内部,Hiredis根据不一样的参数分割命令转化为操做redis数据库的标准命令,你能够格式化多个参数来构造redis的命令,以下
reply = redisCommand(context, "SET key:%s %s", myid, value);
当命令被成功执行后redisCommand会有相应的返回值。若是有错误发生,返回值为NULL而且redisReply结构体中的err变量将会被设置成相应的值(请参照Errors章节)。一旦有错误发生context不能被重用而且你须要创建一个新的链接。
redisCommand执行后返回值类型为redisReply。经过redisReply结构体中的type变量能够肯定命令执行的状况。
REDIS_REPLY_STATUS:
返回执行结果为状态的命令。好比set命令的返回值的类型是REDIS_REPLY_STATUS,而后只有当返回信息是"OK"时,才表示该命令执行成功。能够经过reply->str获得文字信息,经过reply->len获得信息长度。
REDIS_REPLY_ERROR:
返回错误。错误信息能够经过reply->str获得文字信息,经过reply->len获得信息长度。
REDIS_REPLY_INTEGER:
返回整型标识。能够经过reply->integer变量获得类型为long long的值。
REDIS_REPLY_NIL:
返回nil对象,说明不存在要访问的数据。
REDIS_REPLY_STRING:
返回字符串标识。能够经过reply->str获得具体值,经过reply->len获得信息长度。
REDIS_REPLY_ARRAY:
返回数据集标识。数据集中元素的数目能够经过reply->elements得到,每一个元素是个redisReply对象,元素值能够经过reply->element[..index..].*形式得到,用在获取多个数据结果的操做。
执行完命令调用后应该经过freeReplyObject()释放redisReply,对于嵌套对象(好比数组)要注意,并不须要嵌套进行释放,这样是有害的会形成内存破坏。
Important:hiredis当前版本 (0.10.0)当使用异步API时会本身释放replies对象。这意味着你使用异步API时并不须要主动调用freeReplyObject 。relpy对象当回调返回时将会被自动释放。可是这种行为也许会在未来的版本中改变,因此升级时请密切关注升级日志。
断开链接而且释放context使用如下函数
void redisFree(redisContext *c);
此函数立马关闭socket而且释放建立context时分配的资源。
和redisCommand函数类似,redisCommandArgv函数能够用于传输多个命令参数。函数原型为
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
,相似于 lpush, del key1 key2..., zadd key score1 member1 score2 member2...这类命令, 其中 argc是传递参数的个数, argv主要用于传递的string的value, 而argvlen 是每一个string的size。
此函数返回值与redisCommand类似。参考https://gist.github.com/dspezia/1455082
为了搞清楚Hiredis在阻塞链接下的管线操做,须要理解其内部执行过程。
当任何相似于redisCommand的函数被调用,Hiredis首先将命令格式化为redis支持的命令协议。被格式化后的命令被放入context的输出缓冲区,这个缓冲区是动态的,因此它能够容纳任意数量的命令。在命令进入输出缓冲区后,redisGetReply 函数被调用。这个函数有如下两种执行方式:
输入缓冲区非空:
从输入缓冲区中尝试解析单独的reply对象而且返回reply
若是没有reply能被解析,执行步骤2
输入缓冲区为空:
将整个输出缓冲区写入socket
从socket中读取数据直到有一个reply能被解析
Hiredis为了有效利用socket还提供了redisGetReply的接口。对于管线命令,须要完成的惟一事情就是填充输出缓冲区。有两个函数被用于执行此操做,这两个函数基本与redisCommand函数功能相似,可是他们不返回reply
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
当这两个函数被一次或屡次调用时,经过redisGetReply依次返回replies。redisGetReply的返回值要么是REDISOK或是REDISERR,REDIS_ERR意味着得到reply发生了错误,想要获得具体的错误缘由能够经过err变量来获取。
如下经过一个简单例子说明管线的使用:
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);
redisGetReply这个API也能够被用来实现阻塞的订阅模式
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK)
{
// consume message
freeReplyObject(reply);
}
若是某些函数(如redisConnect, redisCommand(调用不成功,函数返回值为NULL或者REDIS_ERR,此时context结构体中的err成员为非零值,可能为如下几种常量
REDIS_ERR_IO:当建立链接时(试着写socket或者读socket)发生的I/O错误。若是你在代码中包含了errno.h头文件,你便能获得准确的错误码。
REDIS_ERR_EOF:redis服务端已经关闭了此链接。
REDIS_ERR_PROTOCOL:服务端解析协议时发生了错误。
REDIS_ERR_OTHER:其余错误。目前仅仅表示没法解析目标主机名的错误。
在错误状况下,能够经过context结构体中的errstr成员获得错误的确切描述。
Hiredis自带的异步API很容易和一些基于事件的库结合使用。好比和libev、ibevent的结合使用。
函数redisAsyncConnect被用来和redis创建非阻塞链接。它返回redisAsyncContext的结构体,结构体的err成员用来检查在建立链接的过程当中是否发生了错误。由于建立的是非阻塞的链接,内核并不能立马返回一个链接指定主机的结果。
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err)
{
printf("Error: %s\n", c->errstr);
// handle error
}
异步Context可设置一个响应断开链接事件的回调函数,当链接断开时会相应执行。回调函数的原型为
void(const redisAsyncContext *c, int status);
在断开链接的状况下,当链接是由用户本身断开的status参数为REDISOK,若是出现了其余错误status参数为REDISERR,当错误时经过err成员能够获得准确的错误码。
当回调执行完毕后context对象会本身释放资源。此事件的回调函数给你建立一个新链接提供了便利。
一个context对象仅能设置一次断开链接的回调,若是再进行下一次设置将会返回REDIS_ERR。设置断开链接回调函数的原型为:
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
在异步的状况下,redis的操做指令将会被自动加入事件循环队列。因为发送命令执行的过程是异步的,当命令执行完毕后将会调用相应的回调函数。回调函数的原型为
void(redisAsyncContext *c, void *reply, void *privdata);
privdata参数是由调用者本身定义的数据类型。
如下是进行异步命令操做的函数:
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,const char *format, ...);
int redisAsyncCommandArgv( redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
命令的使用方式和上面所讲的同步接口相似。执行成功返回REDIS_OK,不然返回REDIS_ERR。好比链接已经关闭的状况下再使用redisAsyncCommand向此链接执行命令就会返回REDIS_ERR。
回调执行完毕后若是reply不为空,回调执行完毕后将自动对reply的资源进行回收。
当context发生错误时回调获得的reply将为空。
一个异步的链接能够经过下面这个函数终止
void redisAsyncDisconnect(redisAsyncContext *ac);
当这个函数被调用时,链接并不会被当即关闭,而是等待全部这个链接的异步命令操做执行完毕,而且回调事件已经执行完毕后才关闭此链接,这时在响应关闭链接事件的回调函数中获得的状态为REDIS_OK,此链接的资源也将会被自动回收。
在context对象被建立后进行不多的几步操做就能够进行挂接。参看目录adapters/下是如何挂接到libev 和 libevent下的。
Hiredis自带的答复解析API ,能够很容易与更高层次的语言进行绑定。 TODO: