Redis源码解析:18Hiredis同步API和回复解析API代码解析

本文主要介绍了Hiredis的同步操作API和回复解析API。同步操作API包括TCP建链、发送命令和接收回复,其中redisCommand函数方便地发送命令。回复解析API涉及输入缓存和解析过程,通过redisReader结构解析Redis的回复信息,构建redisReply结构树。解析过程中,redisReaderGetReply函数根据输入缓存内容生成redisReply结构,用于表示Redis的多种回复类型。
摘要由CSDN通过智能技术生成

         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中。代码较简单,不在赘述。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值