Hiredis中的异步API函数需要与事件库(libevent,libev, ev)一起工作。因为事件循环的机制,异步环境中的命令是自动管道化的。因为命令是异步发送的,因此发送命令时,必要情况下,需要提供一个回调函数,以便在收到命令回复时调用该函数。
异步API涉及到的函数分别是:
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
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);
void redisAsyncDisconnect(redisAsyncContext *ac);
以上函数分别对应TCP建链、发送命令和TCP断链。
一:异步上下文
类似于同步操作API,异步操作API中也有一个上下文结构redisAsyncContext,用于维护异步链接中的各种状态。
redisAsyncContext结构在同步上下文结构redisContext的基础上,增加了一些异步属性,它的定义如下:
typedef struct redisAsyncContext {
/* Hold the regular context, so it can be realloc'ed. */
redisContext c;
/* Setup error flags so they can be used directly. */
int err;
char *errstr;
/* Not used by hiredis */
void *data;
/* Event library data and hooks */
struct {
void *data;
/* Hooks that are called when the library expects to start
* reading/writing. These functions should be idempotent. */
void (*addRead)(void *privdata);
void (*delRead)(void *privdata);
void (*addWrite)(void *privdata);
void (*delWrite)(void *privdata);
void (*cleanup)(void *privdata);
} ev;
/* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
redisDisconnectCallback *onDisconnect;
/* Called when the first write event was received. */
redisConnectCallback *onConnect;
/* Regular command callbacks */
redisCallbackList replies;
/* Subscription callbacks */
struct {
redisCallbackList invalid;
struct dict *channels;
struct dict *patterns;
} sub;
} redisAsyncContext;
该结构的第一个属性就是同步上下文结构redisContext c,剩下的就是一些异步属性:
结构体ev中包含了,当Hiredis异步API与事件库(libev,libevent, Redis ev)一起工作时,用于注册和删除读写事件的函数;
回调函数onDisconnect,表示断链时会调用的函数,该属性可以通过redisAsyncSetDisconnectCallback函数设置;
回调函数onConnect,表示TCP建链成功或失败之后会调用的函数,该属性可以通过redisAsyncSetConnectCallback函数设置;
replies属性是一个redisCallbackList结构,也就是由回调结构redisCallback组成的单链表。当发送普通命令时,会依次将该命令对应的回调结构追加到链表中,当Redis服务器回复普通命令时,会依次调用该链表中的每个redisCallback结构中的回调函数;
结构体sub用于处理订阅模式,其中的字典channels,以频道名为key,以回调结构redisCallback为value。当客户端使用Hiredis异步API发送”subscribe”命令后,服务器产生回复时,就会根据回复信息中的频道名查询字典channels,找到对应的回调结构,调用其中的回调函数。字典patterns与channels类似,只不过它用于”psubscirbe”命令,其中的key是频道名模式;回调链表invalid,当客户端处于订阅模式下,服务器发来了意想不到的回复时,会依次调用该链表中,每个回调结构中的回调函数。
二:建链
函数redisAsyncConnect执行异步操作中的TCP建链。
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
redisContext *c;
redisAsyncContext *ac;
c = redisConnectNonBlock(ip,port);
if (c == NULL)
return NULL;
ac = redisAsyncInitialize(c);
if (ac == NULL) {
redisFree(c);
return NULL;
}
__redisAsyncCopyError(ac);
return ac;
}
该函数首先根据ip和port,调用redisConnectNonBlock函数向Redis服务器发起非阻塞的建链操作,然后调用redisAsyncInitialize函数创建异步上下文结构redisAsyncContext。
redisAsyncSetConnectCallback函数用于设置异步上下文中的建链回调函数。其代码如下:
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) {
ac->onConnect = fn;
/* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event
* library functions are already set. */
_EL_ADD_WRITE(ac);
return REDIS_OK;
}
return REDIS_ERR;
}
该函数中,如果之前已经设置过建链回调函数了,则直接返回REDIS_ERR。
该函数除了设置异步上下文中的建链回调函数之外,还会调用_EL_ADD_WRITE,注册可写