Redis 客户端 Hiredis 简介

Redis 客户端 Hiredis 简介学习总结一下官方发布的 C 版本客户端 hiredis 了解 hiredis 客户端大致实现细节 在理解代码之间需要了解通信协议的特点 我上一篇转载的文章已经有过介绍 大家可以去看一下 hiredis 提供了同步 异步访问 异步 API 需要与一些事件库协同工作 主要看一下同步 API 的实现 hiredis 与服务端通信的 API 比较简单 主要有这几个步骤 建立连接发送命令等待结果并处理

学习总结一下官方发布的C版本客户端 hiredis,了解hiredis 客户端大致实现细节。在理解代码之间需要了解通信协议的特点,我上一篇转载的文章已经有过介绍,大家可以去看一下。

hiredis 提供了同步、异步访问,异步 API 需要与一些事件库协同工作,主要看一下同步API的实现。

hiredis 与服务端通信的API比较简单,主要有这几个步骤:

  • 建立连接
  • 发送命令
  • 等待结果并处理
  • 释放连接

一、相关数据结构

redisContext 保存连接建立后的上下文。 err 保存错误码,如果为0表示没错,如果非0那么错误说明保存在 errstr 中;fd是连接建立后的套接字;flags表示连接的标识;obuf 保存要向 redis-server 发送的命令内容;reader 用来读取从服务端返回的消息,redisReader中的buf成员用来保存内容;connection_type 表示连接类型,有两种分别是REDIS_CONN_TCPREDIS_CONN_UNIXtimeout 是连接时指定的超时时间。

/* Context for a connection to Redis */ 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 */ enum redisConnectionType connection_type; struct timeval *timeout; struct { char *host; char *source_addr; int port; } tcp; struct { char *path; } unix_sock; } redisContext;
/* This is the reply object returned by redisCommand() */ typedef struct redisReply { int type; /* REDIS_REPLY_* */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ size_t len; /* Length of string */ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ struct redisReply element; /* elements vector for REDIS_REPLY_ARRAY */ } redisReply;

redisReply 这个结构是保存发送命令后得到的返回结果,type 表示服务器返回结果的类型,比如 REDIS_REPLY_STRING,表示返回一个 string,此时内容就保存在 str 这个成员中,其他成员类似。有下面这几种类型:

#define REDIS_REPLY_STRING 1 #define REDIS_REPLY_ARRAY 2 #define REDIS_REPLY_INTEGER 3 #define REDIS_REPLY_NIL 4 #define REDIS_REPLY_STATUS 5 #define REDIS_REPLY_ERROR 6

(二)相关 api

建立连接

redisContext *redisConnect(const char *ip, int port); redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); redisContext *redisConnectNonBlock(const char *ip, int port); redisContext *redisConnectBindNonBlock(const char *ip, int port, const char *source_addr); redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, const char *source_addr); redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixNonBlock(const char *path); redisContext *redisConnectFd(int fd);

这些 api 都是建立连接的,可以根据需求或者条件选择合适的。连接建立成功返回 redisContext,将连接信息放到这个结构中,通过 err 成员来判断是否建立成功。

发送命令并等待结果

redisCommandredisAppendCommand 系列函数用来向服务器发送命令。

redisCommand -> redisvCommand -> redisvAppendCommand -> __redisAppendCommand

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; }

这个函数组装发送的命令到redisContextobuf 中,obuf 可以动态扩容,所以不用担心溢出;接下来在函数 redisCommand 中调用 __redisBlockForReply,继而调用 redisGetReply 这个函数来获取返回结果。

int redisGetReply(redisContext *c, void reply) { int wdone = 0; void *aux = NULL; /* Try to read pending replies */ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ if (aux == NULL && c->flags & REDIS_BLOCK) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) return REDIS_ERR; } while (!wdone); /* Read until there is a reply */ do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } /* Set reply object */ if (reply != NULL) *reply = aux; return REDIS_OK; }

redisAppendCommand 这个函数将发送的命令格式化放入 redisContextobuf 中,可以一次发送多条命令,然后接收一批结果,结果按照发送命令的顺序保存,这里利用了 pipeline 特性,可以减少网络传输的次数,提高IO吞吐量。

释放连接

使用完一个连接后需要调用 redisFree 函数来释放这个连接,主要是关闭套接字,释放申请的缓冲区。

void redisFree(redisContext *c) { if (c == NULL) return; if (c->fd > 0) close(c->fd); sdsfree(c->obuf); redisReaderFree(c->reader); free(c->tcp.host); free(c->tcp.source_addr); free(c->unix_sock.path); free(c->timeout); free(c); }

一个例子

下面给出一个例子,对这几个API最基本的使用,测试的时候需要先安装 redis-server 并启动,默认端口为 6379,同时需要安装 hiredis 库,sudo yum install hiredis-devel,或者源码安装。

/* > File Name: redis-cli.c > Author: Tanswer_ > Mail: > Created Time: Thu Jun 28 15:49:43 2018 / #include 
    #include 
    #include 
    #include 
    int main() { redisContext* c = redisConnect((char*)"127.0.0.1", 6379); if(c->err){ redisFree(c); return 0; } printf("connect redis-server success.\n"); const char* command = "set good luck"; redisReply* r = (redisReply*)redisCommand(c, command); if(r == NULL){ redisFree(c); return 0; } if(!(r->type == REDIS_REPLY_STATUS && strcasecmp(r->str, "OK") == 0)){ printf("Failed to execute command[%s].\n", command); freeReplyObject(r); redisFree(c); return 0; } freeReplyObject(r); printf("Succeed to execute command[%s].\n", command); const char* command1 = "strlen good"; r = (redisReply*)redisCommand(c, command1); if(r->type != REDIS_REPLY_INTEGER){ printf("Failed to execute command[%s].\n", command1); freeReplyObject(r); redisFree(c); return 0; } int length = r -> integer; freeReplyObject(r); printf("The length of 'good' is %d.\n", length); printf("Succeed to execute command[%s].\n", command1); const char* command2 = "get good"; r = (redisReply*)redisCommand(c, command2); if(r -> type != REDIS_REPLY_STRING){ printf("Failed to execute command[%s].\n", command2); freeReplyObject(r); redisFree(c); return 0; } printf("The value of 'goo' is %s.\n", r->str); freeReplyObject(r); printf("Succeed to execute command[%s].\n", command2); redisFree(c); return 0; }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214764.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午3:30
下一篇 2026年3月18日 下午3:30


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号