本文翻译自Hiredis官方,README.md文件。
Hiredis是Redis数据库的简约C客户端库。它是简约的,因为它只是增加了对协议的
最小支持,但是同时它使用了一个高级别的printf-like API,所以对于习
惯了printf风格的C编程用户来说,其非常容易使用,而且API中没有明确的绑定 每
个Redis命令。
除了支持发送命令和接收回复之外,它还附带了一个与I / O层分离的回复解析器。
为了方便复用,其设计成为一个流解析器,例如可以在更高级别的语言
绑定中使用,以实现高效的回复解析。
Hiredis仅支持二进制安全(binary-safe)的Redis协议,因此您可以将其与任何
Redis版本(> = 1.2.0)配合使用。
该库带有多个API。 有同步API,异步API和答复解析API。
要使用SynchronousAPI,只需要引入几个函数调用:
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);
函数redisConnect
用于创建所谓的redisContext
结构。redisContext
是
Hiredis
持有连接状态的地方。当连接处于错误状态时,redisContext
结构的整
数err
字段不为零。errstr
字段将包含一个描述错误的字符串。 有关错误的更多
信息可以在Errors
部分找到。 尝试使用redisConnect
连接到Redis
后,应检
查err
字段以查看建立连接是否成功:
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("Error: %s\n", c->errstr);
// handle error
} else {
printf("Can't allocate redis context\n");
}
}
注意:一个redisContext结构是线程非安全的。
有几种方法可以向Redis
发出命令。 首先介绍的是redisCommand
。 这个函数
的格式类似于printf
。 最简单的形式是这样使用的:
reply = redisCommand(context, "SET foo bar");
说明符%s
在命令中插入一个字符串,并使用strlen
来确定字符串的长度:
reply = redisCommand(context, "SET foo %s", value);
当您需要在命令中传递二进制安全字符串时,可以使用%b
说明符。 与指向字符
串的指针一起,它需要字符串的size_t
长度参数:
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
在内部,Hiredis
将命令拆分为不同的参数,并将其转换为与Redis
进行通信的
协议。 一个或多个空格分隔参数,因此您可以在参数的任何位置使用说明符:
reply = redisCommand(context, "SET key:%s %s", myid, value);
当命令成功执行时,redisCommand
的返回值保留一个回复。 发生错误时,返回
值为NULL
,上下文中的err字段将被设置(请参阅Errors
部分)。 **一旦错
误返回,上下文不能被重用,你应该建立一个新的连接。**
redisCommand
的标准回复类型是redisReply
。 应该使用redisReply
中的类
型字段来测试收到的答复类型:
REDIS_REPLY_STATUS
:
reply-> str
来访问。 这 reply-> len
来访问。REDIS_REPLY_ERROR
:
REDIS_REPLY_STATUS
来获 REDIS_REPLY_INTEGER
:
long long
类型的reply-> integer
字 REDIS_REPLY_NIL
:
REDIS_REPLY_STRING
:
reply-> str
来访问。 这个字 reply-> len
来访问。REDIS_REPLY_ARRAY
:
reply->elements
。 redisReply
对象,可以通过reply->
element [.. index ..]
进行访问。 Redis可能会回应嵌套数组,但这也是完全支 应使用freeReplyObject()
函数释放回复。 请注意,这个函数将负责释放包含在数
组和嵌套数组中的子回复对象,所以用户不需要释放子回复(这实际上是有害的,
会损坏内存)。
**重要提示:hiredis(0.10.0)版本在使用异步API时会主动释放回复对象。
这意味着当你使用这个API时你不应该调用freeReplyObject。 在回调返回之后,回
复对象被hiredis清除。 这种行为在未来的版本中可能会发生变化,因此请务必在升
级时密切关注更新日志(请参阅问题#39)。**
要断开和释放上下文,可以使用以下功能:
void redisFree(redisContext *c);
这个函数立即关闭套接字,然后释放创建上下文的分配。
与redisCommand
功能类似一起,函数redisCommandArgv
也可用于发出命
令。 它有以下原型:
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const
size_t *argvlen);
它需要参数的数量argc
,一个字符串数组argv和参数数组argv
的长度。 为了方
便,argvlen
可以设置为NULL
,函数将在每个参数上使用strlen
来确定它的
长度。 显然,当任何参数需要binary-safe时,应提供整个参数数组的长度。
返回值与redisCommand
具有相同的语义。
为了解释Hiredis
如何支持阻塞连接中的流水线,需要了解内部执行流程。
当redisCommand
系列中的任何函数被调用时,Hiredis
首先根据Redis协议格
式化命令,格式化的命令然后被放入输出缓冲区下, 这个输出缓冲区是动态的,所以它
可以容纳任意数量的命令。将命令放入输出缓冲区后,然后调用
redisGetReply
。 这个函数遵循两个执行路径:
函数redisGetReply
作为Hiredis 的数据导出API,可以在用于套接字上等待预期回
复的场景。
对于管道命令,唯一需要做的事情是填充输出缓冲区。 为了实现这个功能,可以使
用两个与redisCommand
类似的命令,但它们不返回一个回复:
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
在调用一个或多个上述函数之后,redis Get Reply
可以用来接收随后的答复。这
个函数的返回值是REDIS_OK
或REDIS_ERR
,其中后者意味着在处理答复时发
生错误。 正如其他命令一样,上下文中的“err”字段可用于找出导致此错误的原因。
下面的例子显示了一个简单的流水线(导致一次调用write(2)
和一次调用read
):
(2)
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);
This API can also be used to implement a blocking subscriber:
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
// consume message
freeReplyObject(reply);
}
这个API也可以用来实现阻塞的用户订阅:
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
// consume message
freeReplyObject(reply);
}
当一个函数调用不成功时,根据函数返回值为“NULL”或“REDIS_ERR”。 上下文中
的“err”字段将不为零,并设置为其中的一个以下常量:
REDIS_ERR_IO
创建连接时其试图写入到套接字或从套接字读取数据,但该操作出现I/O了错误。
如果你包了errno.h
在你的应用程序中,你可以使用全局的errno
变量来找出是
什么错误。
REDIS_ERR_EOF
服务器关闭导致一次空的读取。
REDIS_ERR_PROTOCOL
解析Redis协议时出现问题。
REDIS_ERR_OTHER
其他类型的错误。目前,其仅使用于指定的主机地址无法解析的情况下。
在任何情况下,上下文中的errstr字段将被设置为保存字符串形式的错误提示。
Hiredis提供了一个异步API,可以轻松地与任何事件库一起工作。可以参考Hiredis
使用的事件库为: libev或[libevent]
(http://monkey.org/~provos/libevent/).
函数redisAsyncConnect
可以用来建立一个与Redis的非阻塞连接。它返回一个指
向新创建的redisAsyncContext
结构的指针。err
字段应在创建后检查,看看是
否有错误的创建连接。**由于要创建的连接是非阻塞的,因此如果指定的主机
和端口能够接受连接,内核将无法立即返回。**
注意:一个redisAsyncContext
是非线程安全的
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
redisAsyncContext
可以保存链接异常处理回调函数,用于处理链接断开时的事
务(可能由于链接错误或是用户主动要求断开链接)。回调函数的原型如下:
void(const redisAsyncContext *c, int status);
在断开连接时,当用户主动断开连接时status
参数被设置为REDIS_OK
,当断线
是由错误引起时,status
参数被设置为REDIS_ERR
。当它是REDIS_ERR
时,
可以访redisAsyncContext
中的err
字段来找出错误的原因。断开连接回调
后,redisAsyncContext
总是被释放。 当需要重新连接时,上述的回调函数是一个
很好的选择。
一个redisAsyncContext
只能被设置一次处理链接断开的回调函数,其重复设置
将导致REDIS_ERR
。设置回调函数的函数原型如下:
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac,
redisDisconnectCallback *fn);
异步上下文中,由于事件循环的性质,命令会自动进行流水线处理。因此,与同步
API不同,发送命令只有一种方法。因为命令是异步发送给Redis的,所以发出一个
命令需要一个回调函数在收到回复时调用。 回复回调应该有以下原型:
void(redisAsyncContext *c, void *reply, void *privdata);
参数private
可以保存任意需要传递给回调函数的数据,数据可以来自命令开始执
行入队到开始执行回调函数之间的任意数据。
可用于在异步上下文中发出命令的函数是:
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);
这两个功能都像阻塞的API那样工作。 命令返回值是REDIS_OK
表示已成功添加到
输出缓冲区,否则就是REDIS_ERR
。例如,当每个用户请求断开连接时,不会有
新的命令添加到输出缓冲区,并且在调用redisAsyncCommand
系列函数时返回
REDIS_ERR
。
如果读取了具有NULL
回调的命令的回复,则立即释放该回复。当一个命令的回
调不是NULL
时,内存在回调后立即被释放:回复数据仅在回调期间有效。
当上下文遇到错误时,所有挂起的回调函数都会得到NULL
的回复。
异步连接可以使用以下命令来终止:
void redisAsyncDisconnect(redisAsyncContext *ac);
当这个函数被调用时,连接是not立即终止。相反,新命令不再被接受,连接
只有在所有待处理的命令已经写入套接字,并且它们各自的应答已经被读取并且它
们各自的回调已经被执行,连接才会终止。之后,断开回调以“REDIS_OK”状态执
行,上下文对象被释放。
上下文对象创建后需要设置几个钩子函数。参考与libev和libevent绑定的
apapters/
。
Hiredis提供了回复解析API,可以轻松编写更高级别的语言绑定。
回复解析API由以下功能组成:
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *reader);
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
int redisReaderGetReply(redisReader *reader, void **reply);
在创建正常的Redis上下文时,hiredis在内部使用相同的一组函数,上面的API只是
将其公开给用户以供直接使用。
函数redisReaderCreate
创建一个redisReader
结构,它为协议解析器保存一个
缓冲区,其中包含未分析的数据和状态。
传入数据 - 很可能来自套接字 - 可以使用redisReaderFeed
放置在redisReader
的内部缓冲区中。 这个函数会对由buf
指向的长度为len
缓冲区做一个拷贝 这
个数据在调用redisReaderGetReply
时被解析。 这个函数通过void ** reply
返
回一个整数状态和一个回复对象(如上所述)。 返回的状态可以是REDIS_OK
或
REDIS_ERR
,后者意味着出了问题(协议错误或内存不足错误)。
解析器将多个批量有效负载的嵌套级别限制为7.如果多重批量嵌套级别高于此值,
解析器将返回一个错误。
函数redisReaderGetReply
创建redisReply
,并使函数参数reply
指向创建
redisReply
变量。 例如,如果“REDIS_REPLY_STATUS”类型的响应,
则“redisReply”的“str”字段将保持状态为香草C字符串(a vanilla C string
)。 但是,
负责创建redisReply
实例的函数可以通过在redisReader
结构中设置fn
字段
来定制。 这应该在创建redisReader
之后立即完成。例如,[hiredis-rb](https://
github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c),使用自定
义的回复对象函数来创建
当直接使用Reader API或通过普通的Redis上下文间接使用Reader API时,
redisReader结构使用缓冲区来累积服务器的数据。
通常这个缓冲区在空并且大于16 KiB的时候被释放,以避免在未使用的缓冲区中浪
费内存。
但是,当处理非常大的有效载荷时,破坏缓冲区可能会显着降低性能,因此可以修
改空闲缓冲区的最大大小,将读取器结构的maxbuf字段的值更改为所需的值。 0的
特殊值意味着空闲缓冲区没有最大值,所以缓冲区永远不会被释放。
例如,如果你有一个正常的Redis上下文,你可以设置最大空闲缓冲区为零(无
限),只需:
context->reader->maxbuf = 0;
这应该只是为了在处理大型有效载荷时使性能最大化。 应尽快将上下文重新设置为
REDIS_READER_MAX_BUF,以防止分配无用的内存。
Hiredis是由Salvatore Sanfilippo(gmail的antirez)和Peter Noordhuis(gmail的
pcnoordhuis)编写的,并且在BSD许可下发布。
Hiredis目前由Matt Stancliff(genges dot com的matt)和Jan-Erik Rediger
(fnordig dot com的janerik)负责维护