Redis的sentinel模式使用了Hiredis代码,Hiredis是redis数据库一个轻量级的C语言客户端库。它实现的向Redis发送命令的API函数redisCommand,使用方法类似于printf。因此只要熟悉redis命令,就可以很容易的使用该函数将redis命令字符串,转换成统一请求协议格式之后,发送给Redis服务器。
Hiredis库包含三类API:同步操作API、异步操作API和回复解析API。本文主要介绍同步操作API和回复解析API,下一篇介绍异步操作API。
一:同步操作API
所谓的同步操作,就是以阻塞的方式向Redis服务器建链,发送命令,接收命令回复。使用同步操作API,主要涉及以下三个API函数:
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);
1:TCP建链
redisConnect函数创建一个上下文结构redisContext,并向Redis服务器发起TCP建链。该函数是同步建链API,因此该函数返回后,要么TCP已经建链成功了,要么建链期间发生了错误,可以通过检查redisContext结构的err和errstr属性得到错误类型和错误类型。
redisConnect的代码较简单,如下:
redisContext *redisConnect(const char *ip, int port) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags |= REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,NULL);
return c;
}
redisContext上下文结构用于保存所有与Redis服务器连接的状态。比如socket描述符,输出缓存,回复解析器等。该结构的定义如下:
typedef struct redisContext {
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
int fd;
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
} redisContext;
属性err为非0时,表示与Redis服务器的连接发生了错误,属性errstr就包含一个描述该错误的字符串。因此,每次与Redis进行交互之后,就需要检查该属性判断是否发生了错误,一旦有错误发生,则立即终止与Redis的链接。
属性fd就是与Redis服务器链接的socket描述符;flags表示客户端标志位,表示客户端当前的状态;
obuf就是输出缓存,当用户调用redisCommand向Redis发送命令时,命令字符串首先就是追加到该输出缓存中;
reader是一个回复解析器,后续在“回复解析API”中会详细介绍。
2:发送命令,接收回复
用户可以调用redisCommand函数向Redis服务器发送命令,该函数返回Redis的回复信息。该函数的原型如下:
void *redisCommand(redisContext *c, const char *format, ...)
该函数类似于printf,支持不定参数,使用起来很方便,比如:
reply = redisCommand(context, "SET foo bar");
redisCommand函数返回NULL表示发生了错误,可以通过检查redisContext结构中的err得到错误类型;如果执行成功,则返回值是一个指向redisReply结构的指针,其中包含了Redis的回复信息。
可以在格式字符串中使用”%s”,表示在命令中插入一个字符串,此时使用strlen判断字符串的长度:
reply = redisCommand(context, "SET foo %s", value);
如果需要在命令中传递二进制安全的字符串,可以使用”%b”,此时需要一个size_t类型的参数表示该字符串的长度:
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
redisCommand主要是通过redisvCommand实现的,而redisvCommand主要是通过redisvAppendCommand和__redisBlockForReply两个函数实现。它们的代码如下:
void *redisvCommand(redisContext *c, const char *format, va_list ap) {
if (redisvAppendCommand(c,format,ap) != REDIS_OK)
return NULL;
return __redisBlockForReply(c);
}
void *redisCommand(redisContext *c, const char *format, ...) {
va_list ap;
void *reply = NULL;
va_start(ap,format);
reply = redisvCommand(c,format,ap);
va_end(ap);
return reply;
}
redisvAppendCommand函数作用是解析用户的输入,并将用户输入的命令字符串转换成Redis统一请求协议的格式,存储到上下文结构redisContext中的输出缓存obuf中,它的代码如下:
int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
char *cmd;
int len;
len = redisvFormatCommand(&cmd,format,ap);
if (len == -1) {
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
return REDIS_ERR;
}
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
free(cmd);
return REDIS_ERR;
}
free(cmd);
return REDIS_OK;
}
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
sds newbuf;
newbuf = sdscatlen(c->obuf,cmd,len);
if (newbuf == NULL) {
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
return REDIS_ERR;
}
c->obuf = newbuf;
return REDIS_OK;
}
redisvAppendCommand首先调用redisvFormatCommand函数,用于解析用户输入的命令字符串,并将字符串转换成协议格式之后,保存在cmd中。然后调用__redisAppendCommand函数,将cmd追加到输出缓存c->obuf中。代码较简单,不在赘述。