Redis - hiredis源码安装和接口使用介绍

一、hiredis源码安装说明

本文创作基于 hiredisv1.2.0版本

1.简介

  • hiredis是一个用于与Redis交互的C语言客户端库。它提供了一组简单易用的API,使开发人员可以轻松地连接到Redis服务器,并执行各种操作,如设置和获取键值对、执行命令、订阅和发布消息等。
  • hiredis的设计目标是高效性和简单性。它使用纯C语言编写,没有外部依赖,可以轻松地与任何C/C++项目集成。它具有轻量级的实现和低延迟的性能,适用于高并发的应用场景。
  • hiredis支持同步和异步的方式与Redis进行通信。同步方式是指客户端发送一个命令后会一直等待Redis的响应,直到响应返回后才继续执行下一个命令。异步方式是指客户端发送命令后可以继续执行其他任务,通过回调函数来处理Redis的响应。
  • 除了基本的Redis操作,hiredis还提供了一些高级功能,如管道操作和事务。管道操作允许一次性发送多个命令到Redis,以减少网络开销。事务可以将一系列命令打包成一个原子操作,保证它们的执行是连续的。

总之,hiredis是一个简单、高效的C语言客户端库,使开发人员可以轻松地与Redis进行交互。它适用于任何需要与Redis集成的C/C++项目,并且具有良好的性能和灵活性。

2.下载源码

hiredis官网:https://redis.io/lp/hiredis/
hiredis github:https://github.com/redis/hiredis/releases

3.安装说明

# 下载软件包,解压并移动目标位置
[root@Ali ~]# wget https://github.com/redis/hiredis/archive/refs/tags/v1.2.0.tar.gz
[root@Ali ~]# tar xzvf v1.2.0.tar.gz
[root@Ali ~]# mv hiredis-1.2.0 /usr/local/redis/hiredis# 安装依赖
[root@Ali hiredis]# yum install openssl-devel -y# 编译安装 hiredis
[root@Ali hiredis]# make
[root@Ali hiredis]# make install
mkdir -p /usr/local/include/hiredis /usr/local/include/hiredis/adapters /usr/local/lib
cp -pPR hiredis.h async.h read.h sds.h alloc.h sockcompat.h /usr/local/include/hiredis
cp -pPR adapters/*.h /usr/local/include/hiredis/adapters
cp -pPR libhiredis.so /usr/local/lib/libhiredis.so.1.1.0
cd /usr/local/lib && ln -sf libhiredis.so.1.1.0 libhiredis.so && ln -sf libhiredis.so.1.1.0 libhiredis.so.1
cp -pPR libhiredis.a /usr/local/lib
mkdir -p /usr/local/lib/pkgconfig
cp -pPR hiredis.pc /usr/local/lib/pkgconfig

可以看到,make install成功后:

  • hiredis头文件 安装放在/usr/local/include/hiredis
  • 库文件放在 /usr/local/lib/ 目录下 adapters

注意

  • 使用上述编译生成的so不支持SSL,若想支持SSL需要再编译增加USE_SSL=1参数,如:make USE_SSL=1 && make install USE_SSL=1 【参考 hiredis-README】

二、hiredis 使用说明

值得说明的是:hiredis支持 同步异步 两种调用方式。无论同步还是异步,使用hiredis 基本流程都是以下三个步骤:

  1. 使用 redisConnect 连接数据库
  2. 使用 redisCommand 执行命令
  3. 释放对象: 使用 freeReplyObject 释放 redisReply 对象,使用 redisFree 来释放redisContext

1. 简单示例

本小节以一个最简单的 同步API调用 示例,帮助读者建立API使用的整体过程。

一个最简单的API调用为例,至少需要引入以下几个函数:

/* 创建一个redis链接,并返回一个redis上下文 */
redisContext *redisConnect(const char *ip, int port);/* 执行redis操作命令 */
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);/*释放资源*/
void freeReplyObject(void *reply);
void redisFree(redisContext *c);

示例代码:

#include <stdio.h>
#include <string.h>
#include <hiredis/hiredis.h>int main(){redisReply *lpReply = nullptr;redisContext *lpContext = nullptr;/* 创建一个redis链接 */lpContext = redisConnect("127.0.0.1", 6379);if (lpContext == NULL || lpContext->err) {if (lpContext) {printf("Error: %s\n", lpContext->errstr);// handle error} else {printf("Can't allocate redis context\n");}}/* 执行redis操作命令 */// void *redisCommand(redisContext *c, const char *format, ...);lpReply = (redisReply*)redisCommand(lpContext, "SET foo %s", "12345");printf("type=%d, value=%d\n", lpReply->type, lpReply->integer);/* 释放一个响应对象 */freeReplyObject(lpReply);lpReply = (redisReply*)redisCommand(lpContext, "GET foo");printf("type=%d, value=%s\n", lpReply->type, lpReply->str);/* 释放一个响应对象 */freeReplyObject(lpReply);/* 是否上下文 */redisFree(lpContext);
}

运行结果:

# 执行前,redis中不存在foo的key
[wengjianhong@Ali testzone]$ redis-cli
127.0.0.1:6379> get foo
(nil)
127.0.0.1:6379># 编译运行
[wengjianhong@Ali testzone]$ g++ test_hiredis.cpp --std=c++11 -lhiredis -o test_hiredis
[wengjianhong@Ali testzone]$ ./test_hiredis
type=5, value=0
type=1, value=12345# 执行后,获取 redis中foo的值
[wengjianhong@Ali testzone]$ redis-cli
127.0.0.1:6379> get foo
"12345"
127.0.0.1:6379>

2.同步API说明

建立链接

/* 建立连接 */
redisContext *redisConnect(const char *ip, int port);/* 带参数的建立连接 */
redisContext *redisConnectWithOptions(const redisOptions *options);/* 重连,重连时自动使用保存在上下文的参数 */
int redisReconnect(redisContext *c);/* 此外,redis还有如下的建立连接的接口,底层上也是调用 redisConnectWithOptions */
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 *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectFd(redisFD fd);

其中,调用建连接口返回redis上下文 redisContext 注意: redisContext不是线程安全的),其中保存hiredis的连接状态。
redisContext 中包含一个非零的整数err字段和一个带有错误描述的字符串字段。

/* Context for a connection to Redis */
typedef struct redisContext {const redisContextFuncs *funcs;   /* Function table */int err;           /* Error flags, 0 when there is no error */char errstr[128];  /* String representation of error when applicable */// ... ...
} redisContext;

在使用redisConnect连接到Redis后,应该检查err字段,看看建立连接是否成功,如:

redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {if (c) {printf("Error: %s\n", c->errstr);// handle error} else {printf("Can't allocate redis context\n");}
}

注意:套接字选项直接应用于底层套接字,不会存储在 redisConnext 中,因此调用 redisReconnect 重连的时候,必须重新设置。如:

int redisEnableKeepAlive(redisContext *c);
int redisEnableKeepAliveWithInterval(redisContext *c, int interval);
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout);

发送命令

hiredis支持多种向Redis发出操作指令的接口,函数原型如下:

/*** @brief           使用格式化字符串* @param c         redis上下文指针* @param format    参数格式化字符串* @param           参数* @return          redisReply 结构体指针
*/
void* redisCommand(redisContext* c, const char* format, ...);/* 使用 argc、argv 参数列表 */
/*** @brief           使用 argc、argv 参数列表* @param c         redis上下文指针* @param argc      参数个数* @param argv      参数指针数组* @param argvlen   参数长度数组* @return          redisReply 结构体指针
*/
void* redisCommandArgv(redisContext* c, int argc, const char** argv, const size_t* argvlen);

上述的三个接口

  • 执行成功时,返回值 void* 实际上是 redisReply* 结构体如下。
  • 执行失败时,返回值为NULL,并且将设置上下文中的err字段(请参阅错误部分)。

注意:一旦返回错误,上下文就不能被重用,您应该建立一个新的连接。

/* 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;

示例:

 示例1
void* lpRelay = redisCommand(context, "SET foo bar");
void* lpRelay = redisCommand(context, "SET foo %s", value);
void* lpRelay = redisCommand(context, "SET %s %s", key, value);
void* lpRelay = redisCommand(context, "SET foo %b", value, (size_t) valuelen); /* 二进制参数 需要指定长度 *//// 示例2
int argc = 3;
const char *argv[] = {"SET", "foo3", "bar3"};
size_t argvlen[] = {strlen(argv[0]), strlen(argv[1]), strlen(argv[2])};
void* lpReply = (redisReply*)redisCommandArgv(lpContext, argc, argv, argvlen);

hiredis 还支持管道的命令方式 和 显示的获取回复

/* Write a command to the output buffer. Use these functions in blocking mode* to get a pipeline of commands. */
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);/* 获取返回结果 */
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);

示例:

// 示例1
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,(void**)&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,(void**)&reply); // reply for GET
freeReplyObject(reply);// 示例2
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {// consume messagefreeReplyObject(reply);
}

释放资源

/* 释放一个响应对象 */
void freeReplyObject(void *reply)/* 释放上下文(自动断开连接) */
void redisFree(redisContext *c);

3.异步API说明

建立连接

调用异步建连接口返回redis异步上下文 redisAsyncContext 注意: redisAsyncContext 不是线程安全的)。此外,使用异步调用还需要设置回调函数。

/* 建立异步连接 */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
/* 建立带参数选项的异步连接 */
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);/* 回调函数指针 */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallbackNC)(struct redisAsyncContext *, int status);
typedef void(redisTimerCallback)(void *timer, void *privdata);/* 设置异步回调函数 */
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);

示例:

void appConnect(myAppData *appData)
{redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);if (c->err) {printf("Error: %s\n", c->errstr);// handle errorredisAsyncFree(c);c = NULL;} else {appData->context = c;appData->connecting = 1;c->data = appData; /* store application pointer for the callbacks */redisAsyncSetConnectCallback(c, appOnConnect);redisAsyncSetDisconnectCallback(c, appOnDisconnect);}
}void appOnConnect(redisAsyncContext *c, int status)
{myAppData *appData = (myAppData*)c->data; /* get my application specific context*/appData->connecting = 0;if (status == REDIS_OK) {appData->connected = 1;} else {appData->connected = 0;appData->err = c->err;appData->context = NULL; /* avoid stale pointer when callback returns */}appAttemptReconnect();
}void appOnDisconnect(redisAsyncContext *c, int status)
{myAppData *appData = (myAppData*)c->data; /* get my application specific context*/appData->connected = 0;appData->err = c->err;appData->context = NULL; /* avoid stale pointer when callback returns */if (status == REDIS_OK) {appNotifyDisconnectCompleted(mydata);} else {appNotifyUnexpectedDisconnect(mydata);appAttemptReconnect();}
}

发送命令

/* Reply回调函数 */
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);/* 异步发送操作命令 */
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);

注意:当命令执行完成发生 Reply回复回调时,回调数据只会在回调函数中有效,一旦回调函数结束指针指向的数据就无效了

释放资源

/* 释放连接 */
void redisAsyncDisconnect(redisAsyncContext *ac);/* 释放异步上下文 */
void redisAsyncFree(redisAsyncContext *ac);

结束语

参考:

  • GitHub上的hiredis项目的 README文档

最后忍不住想吐槽一下:C++的开源项目真的做的不如Python、java好。
Redis作为当前最热门的开源项目之一,hiredis也有很多的用户。然而,hiredis的对外接口也没有很好的函数说明。
在使用过程中一个很大的问题,即:当传入的参数有多个时参数的含义基本靠猜(比如:redisCommandArgv)、函数的返回值使用 void* 后很难知晓其含义(比如:redisCommand

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/838560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言 | Leetcode C语言题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; struct ListNode *reverseBetween(struct ListNode *head, int left, int right) {// 因为头节点有可能发生变化&#xff0c;使用虚拟头节点可以避免复杂的分类讨论struct ListNode *dummyNode malloc(sizeof(struct ListNode));dummyNode…

案例实践 | 招商局集团基于长安链的双循环航运贸易应用

案例名称-招商局双循环航运贸易联盟链 ■ 建设单位 招商局集团 ■ 用户群体 货主企业、物流企业、基础设施运营商等各参与主体 ■ 应用成效 已赋能产业链上下游超1.2万家中小微企业&#xff0c;累计提供普惠金融超830亿元 案例背景 作为全球贸易大国&#xff0c;我国约…

风电功率预测 | 基于CNN卷积神经网络的风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测完整代码风电功率预测 基于卷积神经网络(Convolutional Neural Network, CNN)的风电功率预测可以通过以下步骤实现: 数据准备:收集与风电场发电功率相关的数据,包括风速、风向、温度、湿度等气象数据以及风电场的历史功率数据。 数据预处理:对…

Cadence 16.6 绘制PCB封装时总是卡死的解决方法

Cadence 16.6 绘制PCB封装时总是卡死的解决方法 在用Cadence 16.6 PCB Editor绘制PCB封装时候&#xff0c;绘制一步卡死一步&#xff0c;不知道怎么回事儿&#xff0c;在咨询公司IT后&#xff0c;发现是WIN系统自带输入法的某些热键与PCB Editor有冲突&#xff0c;导致卡死。 …

系统思考—团队学习

结束昨日435期JSTO“探索学习的新视界&#xff1a;硬核工具分享”&#xff0c;有伙伴分享的提升效率的AI工具&#xff0c;也有自我发现团队问题解决的工具&#xff0c;伙伴们都在各自的领域实践、吸收、反馈、复盘。这次的团队学习不仅是知识的传递&#xff0c;更是一场脑力激荡…

线性系统(二)

线性系统&#xff08;二&#xff09; 1.直观理解线性方程组结构2. 不同解的结论3. 更一般的高斯-约旦消元法4.齐次线性方程组 链接: 线性系统&#xff08;一&#xff09; 1.直观理解线性方程组结构 长这样&#xff0c;方程就有解&#xff0c;即相交坐标。 长这样&#xff0c;…

[论文笔记]Corrective Retrieval Augmented Generation

引言 今天带来论文Corrective Retrieval Augmented Generation的笔记&#xff0c;这是一篇优化RAG的工作。 大型语言模型(LLMs) inevitable(不可避免)会出现幻觉&#xff0c;因为生成的文本的准确性不能仅仅由其参数化知识来确保。尽管检索增强生成(RAG)是LLMs的一个可行补充…

Kibana使用

一、什么是Kibana   Kibana 是一个开源的分析和可视化平台&#xff0c;Kibana 提供搜索、查看和与存储在 Elasticsearch 索引中的数据进行交互的功能。开发者或运维人员可以轻松地执行高级数据分析&#xff0c;并在各种图表、表格和地图中可视化数据。 Kibana使用&#xff1a…

Jenkins 忘记登录密码怎么办

在安装Jenkins中遇到忘记登录密码该怎么呢&#xff1f;下面是一个解决办法 1. 先停止jenkins服务 我是用tomcat启动的jenkis 2. 找到config.yaml文件 find / -name config.xml命令执行后找到如下结果&#xff1a; /root/.jenkins/config.xml /root/.jenkins/users/admin_839…

scanf读取标准输入

内容 scanf函数的原理 多种数据类型混合输入 常用的数据输入/输出函数 程序员可以给程序输入数据&#xff0c;程序处理后会返回一个输出。C语言通过函数库读取标准输入&#xff0c;然后通过对应函数处理将结果打印到屏幕上&#xff0c;printf函数可以将结果打印到屏幕上。下…

Java项目:基于ssm框架实现的家政服务网站管理系统分前后台(B/S架构+源码+数据库+毕业论文+答辩PPT)

一、项目简介 本项目是一套基于ssm框架实现的家政服务网站管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 二、技术实现 jdk版本&#xff1a;1.…

Rust构造JSON和解析JSON

目录 一、Rust构造JSON和解析JSON 二、知识点 serde_json JSON 一、Rust构造JSON和解析JSON 添加依赖项 cargo add serde-json 代码&#xff1a; use serde_json::{Result, Value};fn main() -> Result<()>{//构造json结构 cpu_loadlet data r#"{"…

高质量英文文献应该如何查找并且阅读?

1. 查找 使用谷歌学术进行论文关键字检索&#xff0c;查找高度匹配的论文。这里我们可以选择年限等信息进行筛选。作为研究者我们一般选择近三年的文章进行阅读。这里谷歌学术需要科学上网&#xff0c;请大家自行解决。 https://scholar.google.com/ 2. 查看期刊等级 我们查…

计算机视觉与机器学习之文档解析与向量化技术加速多模态大模型训练与应用

目录 前言1、TextIn文档解析技术1.1、文档解析技术1.2、目前存在的问题1.2.1、不规则的文档信息示例 1.3、合合信息的文档解析1.3.1、合合信息的TextIn文档解析技术架构1.3.2、版面分析关键技术 Layout-engine1.3.3、文档树提取关键技术 Catalog-engine1.3.4、双栏1.3.5、非对称…

2024kali linux上安装java8

1 kali下载Java 8安装包 访问Oracle官网或其他可信的Java下载站点&#xff0c;如华为云的开源镜像站&#xff08;例如&#xff1a;https://repo.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz&#xff09;。 确保下载的是与你的Kali Linux系统架构&#xf…

一款专业级别的灵卡技术红外机芯——LC221详解

灵卡科技最新推出的一款高性能红外摄像头组件——LC221。这款独特的设备专为广泛的热点搜索以及热瞄准应用领域而精心打造&#xff0c;旨在为广大用户带来前所未有的智能化体验。下面&#xff0c;让我们详细了解这款新产品的特点与优势吧&#xff01; 首先&#xff0c;让…

挖掘机可视化:工程施工的智能助手

通过图扑 3D 建模和实时数据监控&#xff0c;提供设备操作的全方位视角。操作员可以从屏幕上直观地观察挖掘机各部分的工作状态&#xff0c;实时掌握挖掘进度和设备健康状况。 此技术提升了施工效率&#xff0c;减少了人为误操作风险&#xff0c;同时还支持远程诊断和维护&…

13、24年--信息系统治理——IT审计

1、IT审计基础 1.1 IT审计定义 无重要的考点,自己读课本了解即可。 1.2 IT审计目的 1)IT审计的目的是指通过开展IT审计工作,了解组织IT系统与IT活动的总体状况,对组织是否实现IT目标进行审查和评价,充分识别与评估相关IT风险,提出评价意见及改进建议,促进组织实现IT目…

C语言:指针(3)

1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* ; 本质是把字符串 hello bit. ⾸字符的地址放到了pstr中。上⾯代码的意思是把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。 2. 数组指针变量 2.1 数组指针变量是什么&#xff1f; 答案…

活字格如何复制指定单元格中的内容

1、安装插件“复制到剪贴板”后。 2、在需要执行复制的命令中&#xff0c;选择“复制到剪贴板” 3、选择源单元格。 4、执行看效果。