Hiredis 简介

学习总结一下官方发布的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_TCP 和 REDIS_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 成员来判断是否建立成功。

发送命令并等待结果

redisCommand 和 redisAppendCommand 系列函数用来向服务器发送命令。

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

这个函数组装发送的命令到redisContext 的 obuf 中,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;
}

这个函数分下面几步实现:
- 首先查看 redisReader 结构中的 buf 成员是否有数据,有的话则解析出 reply 并返回,否则进入下面;
- 把 obuf 缓冲区的数据全部写入 c->fd,写完后释放 obuf
- 接下来阻塞读取服务器返回的结果并将其放入redisContext -> redisReader -> buf 缓冲区中,进入下一步;
- 调用 redisGetReplyFromReader 解析 buf 中的数据,并返回reply;获取到的 redisReply 对象需要调用 freeReplyObject 显式释放,否则会泄漏。

redisAppendCommand 这个函数将发送的命令格式化放入 redisContext 的 obuf 中,可以一次发送多条命令,然后接收一批结果,结果按照发送命令的顺序保存,这里利用了 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: 98duxm@gmail.com> Created Time: Thu Jun 28 15:49:43 2018************************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <hiredis/hiredis.h>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;
}

Hiredis 简介相关推荐

  1. Redis 客户端 Hiredis 简介

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

  2. hiredis和rapidjson库的使用小结

    Hiredis 简介 Hiredis 是Redis官方发布的C版本客户端 hiredis库.redis的源码中也有使用hiredis.比如redis-cli和Redis中的哨兵机制和主从机制,集群等都 ...

  3. hiredis使用简介

    hiredis是Redis数据库的C接口,目前只能在Linux下使用,几个基本的函数就可以操作redis数据库了. 一.API简介 1.redisContext* redisConnect(const ...

  4. 《Redis实战》一1.2 Redis数据结构简介

    本节书摘来异步社区<Redis实战>一书中的第1章,第1.2节,作者: [美]Josiah L. Carlson(约西亚 L.卡尔森)译者: 黄健宏 责编: 杨海玲,更多章节内容可以访问云 ...

  5. Hiredis快速入门

    文章目录 Hiredis 一.简介 二.API介绍 1.建立连接 2.写数据库 redisReply结构体 3.释放连接 三.应用示例 Hiredis 一.简介 Hiredis库封装了Redis的C语 ...

  6. 在C++下借助hiredis使用redis数据库

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可. 在C++下借助hiredis使用redis数据库 简介 本文为我在编写C++聊天室项目时使用redis的经验之谈,主 ...

  7. etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理

    1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...

  8. Docker学习(一)-----Docker简介与安装

    一.Docker介绍 1.1什么是docker Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植 ...

  9. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

最新文章

  1. linux虚拟网络设备之tun/tap(一)
  2. 项目打包去掉调试时的NSLog、print
  3. 一次线上redis报障处理过程
  4. 互联网经济与实体经济
  5. 机器学习之生成学习算法
  6. CSS使用display:incline与float:left的区别:脱离文档流 参差不齐
  7. 1041 考试座位号 (15分)——17行代码AC
  8. mybatis oracle trim,Mybatis trim标签
  9. 漫步微积分七——连续函数
  10. 汇丰银行是哪个国家的
  11. 三元组法矩阵加法java_C语言实现矩阵加法、减法、乘法和数乘运算
  12. bootstrap 右自适应 左_Bootstrap自适应布局-网格系统
  13. Android 上百实例源码分析以及开源分析
  14. Python分析【崩坏学园2】凝魔纹心所输出
  15. APP如何上架App Store?
  16. 崂山道士、Harry Potter和人脸识别
  17. 解决ios的webview中上/下拉露出黑灰色背景问题
  18. JME3播放背景音乐
  19. Liunx运维整理文案-网络管理命令大全
  20. CTF训练 web安全SSI注入

热门文章

  1. 计算机毕业论文选题推荐|软件工程|系列二
  2. 全国人口净流入城市排名2020_2019年长三角各城市常住人口、城镇化分布:苏宁杭净流入超200万...
  3. 从零开始带你成为JVM实战高手
  4. 集成学习【一】:集成学习(Ensemble Learning)结合神经网络
  5. C#--浮点数取小数点后两位和保留两位
  6. 强烈推荐使用五笔加加输入法
  7. Qt之QUdpSocket定时器网络广播
  8. 青蛙过河 C# 求解
  9. Python如何将仅包含音频内容的Mp4,提取并转换为Mp3
  10. mysql-connector-java各版本直接下载