Linux下的c/c++开发之操作Redis数据库

news2025/5/18 15:47:15

C/C++ 操作 Redis 的常用库

在 C/C++ 开发中操作 Redis 有多种方式,最主流的选择是使用第三方客户端库。由于 Redis 官方本身是使用 C 编写的,提供的 API 非常适合 C/C++ 调用。常见的 Redis C/C++ 客户端库包括:

  • hiredis:官方推荐的轻量级 C 客户端。

  • hiredis-vip:支持 Redis Cluster 的增强版 hiredis。

  • redis-plus-plus:基于 hiredis 的现代 C++ 封装,使用更简洁直观。

hiredis 简介(官方推荐,轻量高效)

hiredis 是 Redis 官方维护的 C 语言客户端库,专注于提供最基本的 Redis 通信支持。它设计简洁,仅包含:

  • 同步命令发送与接收

  • 简单的异步接口(需配合 libevent、libev 使用)

  • 轻量高效、易于嵌入项目中

核心特性

  • 同步与异步 API 支持

  • 占用内存小,编译快速

  • 无多余封装,紧贴 Redis 协议

  • 社区活跃,文档简洁

hiredis-vip 简介(支持 Redis Cluster)

hiredis-vip 是在 hiredis 基础上增强的版本,专门用于支持 Redis Cluster 的自动分片与节点路由功能。它由开源社区维护,并兼容 hiredis 的接口风格。

核心特性

  • 支持 Redis Cluster 的自动路由与重定向处理

  • 封装了 key-slot 映射、MOVED/ASK 重试等逻辑

  • 同样提供同步和异步模式

  • 提供 redisClusterContext 和集群级命令发送接口

redis-plus-plus(又名 sw::redis++)是基于 hiredis 的现代 C++ 封装库,由中国开发者 swz30 编写。它使用 STL 风格设计,提供 RAII、异常处理、泛型接口,更符合现代 C++ 开发习惯。

核心特性

  • 提供类模板支持多种数据类型(如 std::string, std::vector

  • 支持连接池、管道(pipeline)、事务(transaction)

  • 封装 cluster 支持(底层依赖 hiredis-vip)

  • 易用且文档完善,适合快速上手

该篇文章主要介绍hiredis-vip的安装和使用

hiredis-vip的安装

必要依赖

hiredis-vip 依赖官方 hiredis 作为底层 Redis 通信库,因此需要先安装 hiredis

git clone https://github.com/redis/hiredis.git
cd hiredis
make
sudo make install

从github拉取源码编译安装hiredis-vip

git clone https://github.com/vipshop/hiredis-vip.git
cd hiredis-vip
make
sudo make install

安装后我们会得到:

①静态库文件:/usr/local/lib/libhiredis-vip.a   这是你在 C/C++ 项目中需要链接的库。

②头文件:

头文件路径主要功能描述提供的核心类型/函数使用场景是否必需
<hiredis-vip/hiredis.h>单机版 Redis 客户端接口(继承自官方 hiredis)redisContextredisConnect()redisCommand()单个 Redis 实例(非集群)若用单机模式需要
<hiredis-vip/hircluster.h>Redis Cluster 操作的核心接口redisClusterContextredisClusterCommand()分布式 Redis 集群通信集群操作需要
<hiredis-vip/adapters/libevent.h>异步 Redis Cluster 支持,绑定到 libevent 事件循环redisClusterLibeventAttach()异步事件驱动开发(libevent)仅异步需要

<hiredis.h>介绍

结构体redisContext

用于连接上下文

struct redisContext {
    int fd;                  // 套接字描述符
    int flags;               // 状态标志
    char *errstr;            // 错误描述字符串
    int err;                 // 错误码
    void *reader;            // 协议解析器
};

结构体redisReply

所有 Redis 响应的封装结构体

typedef struct redisReply {
    int type;                   // 响应类型,对应 REDIS_REPLY_* 枚举(如 REDIS_REPLY_STRING, REDIS_REPLY_INTEGER 等)

    long long integer;          // 如果 type == REDIS_REPLY_INTEGER,则该字段表示返回的整数值

    int len;                    // 如果 type == REDIS_REPLY_STRING 或 REDIS_REPLY_STATUS 或 REDIS_REPLY_ERROR,则该字段表示 str 的长度(不包含 '\0')

    char *str;                  // 
                                // - 当 type == REDIS_REPLY_STRING 时,str 指向字符串值
                                // - 当 type == REDIS_REPLY_ERROR 时,str 是错误信息
                                // - 当 type == REDIS_REPLY_STATUS 时,str 是状态信息(如 "OK")
                                // - 其他类型该字段为 NULL

    size_t elements;            // 如果 type == REDIS_REPLY_ARRAY,则表示数组中的元素数量

    struct redisReply **element; // 
                                 // - 当 type == REDIS_REPLY_ARRAY 时,该字段指向一个 redisReply* 数组,
                                 //   每个元素是数组中的一个返回项(可递归地是另一个 redisReply)
                                 // - 其他类型该字段为 NULL
} redisReply;

结构体 redisAsyncContext

用于表示一个异步 Redis 客户端上下文

typedef struct redisAsyncContext {
    redisContext c;         // 内部使用的同步上下文(基础连接信息)
    int err;                // 错误码
    char errstr[128];       // 错误信息字符串
    void *data;             // 用户数据
    ...
} redisAsyncContext;

redisReply中的类型枚举值

#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
含义
REDIS_REPLY_STRING字符串类型
REDIS_REPLY_ARRAY数组结果(如 KEYS *
REDIS_REPLY_INTEGER整数类型(如 INCR 结果)
REDIS_REPLY_NIL空值(如不存在的 key)
REDIS_REPLY_STATUS状态,如 "OK"
REDIS_REPLY_ERROR错误

API

①redisConnect

连接 Redis 服务器

redisContext *redisConnect(const char *ip, int port);
  • ip:Redis 服务器 IP 地址,如 "127.0.0.1"

  • port:端口号,如 6379

  • 返回:成功返回 redisContext*,失败 err 字段非零,errstr 描述错误。

②redisConnectWithTimeout

带超时设置的连接方式

redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);

tv:超时时间(秒+微秒),如 {1, 500000} 表示 1.5 秒。

③redisFree

释放连接资源

void redisFree(redisContext *c);

参数:Redis 连接上下文。

④redisCommand

发送命令并同步获取结果

void *redisCommand(redisContext *c, const char *format, ...);
  • format:格式化字符串,如 "SET key %s""INCR %s"

  • 可变参数:要插入的 key、value 等。

  • 返回:指向 redisReply 的指针,需使用 freeReplyObject 释放。

redisReply *reply = (redisReply *)redisCommand(c, "SET %s %s", "mykey", "hello");
⑤freeReplyObject

释放 redisReply 内存

void freeReplyObject(void *reply);
⑥redisCommandArgv

使用参数数组方式发送命令

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
  • argc:参数数量;

  • argv:参数数组;

  • argvlen:每个参数的长度数组;

  • 返回:redisReply*

const char *set_argv[] = {"SET", "mykey", "hello"};
size_t set_argvlen[] = {3, 5, 5};  // 每个参数的长度
redisReply *reply = (redisReply *)redisCommandArgv(c, 3, set_argv, set_argvlen);
 ⑦redisvCommand

同步命令执行函数,支持通过 va_list 传递格式化参数(类似 vprintf

redisReply* redisvCommand(redisContext *c, const char *format, va_list ap);
  • format:格式化字符串,如 "SET key %s""INCR %s"

  • ap:可变参数列表,对应format中的占位符。

  • 返回:指向 redisReply 的指针,需使用 freeReplyObject 释放。

⑧redisAppendCommand

redisAppendCommand 是hiredis提供的发送命令但不立即等待响应的接口。它主要用于实现Redis 命令流水线(pipeline)机制,允许将多个命令连续写入到 Redis 连接的输出缓冲区中,从而一次性发送多个命令,降低网络往返开销。调用该函数后,命令会被追加到输出缓冲区中,真正的响应需要通过 redisGetReply 单独调用来获取。

int redisAppendCommand(redisContext *c, const char *format, ...);

参数说明

  • redisContext *c:Redis 连接上下文,表示当前的 Redis 会话连接。需要通过 redisConnect 或类似函数创建。

  • const char *format:命令格式字符串,与 printf 类似。可以包含格式占位符(如 %s, %d),以支持参数插入。

  • ...(可变参数):与 format 中的占位符对应的实际参数。会自动进行格式替换,生成完整 Redis 命令。

返回值说明

  • 返回 REDIS_OK(即 0):表示命令成功追加到输出缓冲区,等待后续 redisGetReply 获取响应。

  • 返回 REDIS_ERR(即 -1):表示追加命令失败,可能原因包括连接断开、输出缓冲区出错、参数格式不合法等。详细错误信息可通过 c->errstr 查看。

⑨redisvAppendCommand

支持可变参数列表va_list的redisAppendCommand

int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
⑩redisAppendCommandArgv

支持流水线机制的redisCommandArgv

int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
redisGetReply

同步获取Redis服务器响应的函数,redisGetReply 会阻塞当前调用线程,直到从 Redis 服务器接收到完整的一条响应,并将其结果以 redisReply 结构体的形式返回。

注意:如果你通过 redisAppendCommand 追加了多条命令,那么每调用一次 redisGetReply,只会返回最早追加的那一条命令的响应。

int redisGetReply(redisContext *c, void **reply);

参数说明

  • redisContext *c:Redis 连接上下文指针,表示与 Redis 的连接。该连接必须是通过 redisConnectredisConnectWithTimeout 成功建立的。

  • void **reply:这是一个输出参数,用于接收从 Redis 返回的响应对象。传入的指针应当指向一个 redisReply* 类型的变量地址。成功时,*reply 会被赋值为一个新的 redisReply*,调用者需要使用 freeReplyObject() 释放这个对象的内存。

 返回值说明

该函数返回一个整数:

  • 返回 REDIS_OK(即 0)表示成功从服务器读取到响应,并正确解析。此时 *reply 中保存了指向 redisReply 的指针,用户可以根据其类型字段进一步处理数据。

  • 返回 REDIS_ERR(即 -1)表示出现错误,例如网络断开、协议解析失败、连接不可用等。此时可以通过 redisContext 中的 errstr 字段查看具体错误信息。

示例:

#include <iostream>
#include <string>
#include <hiredis-vip/hiredis.h>

int main() {
    // 1. 创建连接
    redisContext* c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        std::cerr << "Connection error: " << (c ? c->errstr : "NULL") << std::endl;
        return -1;
    }

    // 2. 使用 redisCommand 发送 SET 命令
    redisReply* reply = (redisReply*)redisCommand(c, "SET %s %s", "foo", "123");
    if (reply && reply->type == REDIS_REPLY_STATUS) {
        std::cout << "SET result: " << reply->str << std::endl;
    }
    freeReplyObject(reply);

    // 3. 使用 redisCommand 发送 GET 命令
    reply = (redisReply*)redisCommand(c, "GET %s", "foo");
    if (reply && reply->type == REDIS_REPLY_STRING) {
        std::cout << "GET result: " << reply->str << std::endl;
    }
    freeReplyObject(reply);

    // 4. 使用 redisCommandArgv 发送 DEL 命令
    const char* argv[] = {"DEL", "foo"};
    size_t argvlen[] = {3, 3};
    reply = (redisReply*)redisCommandArgv(c, 2, argv, argvlen);
    if (reply && reply->type == REDIS_REPLY_INTEGER) {
        std::cout << "DEL result: " << reply->integer << std::endl;
    }
    freeReplyObject(reply);

    // 5. 释放连接
    redisFree(c);

    return 0;
}

<hircluster.h>介绍

结构体redisClusterContext

  • redisClusterContext 是整个 Redis 集群交互的核心结构,管理了集群拓扑(节点和 slot 映射)、连接信息、重定向机制等。

  • 使用该结构的 API(如 redisClusterCommand)时,内部会根据 key 自动决定使用哪个节点连接并完成请求。

typedef struct redisClusterContext {
    int err;                     // 错误码,0 表示无错误,非 0 表示出错
    char errstr[128];            // 错误信息字符串,用于描述出错原因

    int flags;                   // 标志位,预留扩展,如是否启用 pipeline 等

    // 当前操作所使用的连接节点信息
    redisContext *con;           // 当前正在使用的 hiredis 连接上下文(非集群结构,但内部已支持)

    char *cmd;                   // 上一次执行的命令字符串副本(用于调试或日志)

    struct dict *nodes;          // 所有节点信息(key 为 ip:port,value 为 node 对象)
    struct dict *slots;          // slot 到节点的映射表(0-16383)

    struct list *requests;       // pipeline 请求列表(若使用 pipeline 模式)

    struct timeval *timeout;     // 连接超时设置

    char *err_code;              // 附加的 Redis 错误码字符串(如 MOVED, ASK)

    ..........
} redisClusterContext;

结构体redisClusterAsyncContext

相比普通 Redis,异步集群客户端使用 redisClusterAsyncContext

typedef struct redisClusterAsyncContext {
    redisClusterContext *cc;    // 关联的集群上下文
    void *data;
    ...
} redisClusterAsyncContext;

API

①redisClusterConnect

创建一个 Redis Cluster 上下文并初始化连接信息。

redisClusterContext *redisClusterConnect(const char *nodes, int flags);

参数:

  • nodes:Redis 节点地址字符串,如 "127.0.0.1:7000,127.0.0.1:7001",支持多个地址。

  • flags:连接选项,目前应传 0,为保留字段。

返回值

  • 成功返回 redisClusterContext*

  • 失败返回 NULL,可通过 cc->errcc->errstr 获取错误信息。

②redisClusterSetOptionAddNode

动态添加一个集群节点地址到上下文中,适用于构建连接前动态配置节点。

"动态添加" 指的是在Redis 集群连接已经创建并且正在使用的过程中,后续可以增加新的节点地址到已经存在的 redisClusterContext 中,而无需重新创建或重新连接整个集群。

int redisClusterSetOptionConnectTimeout(redisClusterContext *cc, const struct timeval tv);

参数

  • cc:Redis 集群上下文

  • addr:如 "127.0.0.1:7002"

返回值

  • 成功返回 0,失败返回 -1

③redisClusterSetOptionConnectTimeout

设置连接超时时间。

int redisClusterSetOptionConnectTimeout(redisClusterContext *cc, const struct timeval tv);

参数

  • cc:Redis 集群上下文

  • tvstruct timeval 结构体,单位为秒和微秒(例如 {2, 0} 表示 2 秒)

返回值

  • 成功返回 0,失败返回 -1

处该设置外,其他常用设置有:

函数名称函数原型作用说明
设置连接超时时间int redisClusterSetOptionConnectTimeout(redisClusterContext *cc, struct timeval tv);设置连接 Redis 节点的最大时间,超过即失败(单位:秒+微秒)
设置操作超时时间int redisClusterSetOptionTimeout(redisClusterContext *cc, struct timeval tv);设置执行 Redis 命令的超时时间
设置读取响应超时时间int redisClusterSetOptionReadTimeout(redisClusterContext *cc, struct timeval tv);设置等待 Redis 响应数据的最大时间
设置最大重试次数int redisClusterSetOptionMaxRetries(redisClusterContext *cc, int max_retries);出现网络/重定向等错误时最大重试次数
设置最大重定向次数int redisClusterSetOptionMaxRedirects(redisClusterContext *cc, int max_redirects);设置因 Moved/Ask 错误进行的最多重定向次数
设置错误处理策略int redisClusterSetOptionErrHandling(redisClusterContext *cc, int on_error);设置遇到错误时的处理策略(重试/中断等)
设置认证密码int redisClusterSetOptionPassword(redisClusterContext *cc, const char *password);设置连接 Redis 节点的密码(适用于集群开启密码认证的情况)
启用 SSL 加密int redisClusterSetOptionSsl(redisClusterContext *cc, int ssl_flag);设置是否使用 SSL 连接 Redis(1 开启,0 关闭)
添加初始节点地址int redisClusterSetOptionAddNode(redisClusterContext *cc, const char *ipport);添加用于初始化连接的节点地址,支持多个
④redisClusterConnect2

实际建立 Redis Cluster 的连接,必须在设置完选项后调用。

int redisClusterConnect2(redisClusterContext *cc);

返回值

  • 成功返回 0,失败返回 -1

⑤redisClusterCommand

向 Redis Cluster 发送格式化命令(如 SET %s %s),自动路由到正确的节点。

在 Redis Cluster 模式中,所有的键(key)会被映射到 0~16383 共 16384 个槽(slots)中,每个槽再由集群中的某个节点负责。例如:

  • 节点 A 管理 slot 0~5460

  • 节点 B 管理 slot 5461~10922

  • 节点 C 管理 slot 10923~16383

当你执行命令 SET mykey 123 时:

  • Redis 会对 mykey 做 CRC16 哈希并对 16384 取模,得到一个 slot 编号(例如 slot 9181

  • 然后 Redis Cluster 会根据 slot 映射表,找到哪个节点负责这个 slot

  • 然后把命令发送到对应的节点去执行

hiredis-vip 如何“自动路由”?

hiredis-vip 提供了 redisClusterCommand() 函数,它会:

  1. 自动对 key 做哈希并映射到 slot

  2. 根据 slot,查找当前负责这个 slot 的节点

  3. 自动建立连接(或复用已有连接)并将命令发送给这个节点

  4. 返回对应的 redisReply* 给你,无需你关心命令该发给哪个 IP/端口

你只需写:

redisClusterCommand(cc, "SET %s %s", "mykey", "hello");

hiredis-vip 会自动完成这些步骤,无需你手动计算哈希、选节点。

⑥redisClusterCommandArgv

以参数数组形式向 Redis Cluster 发送命令,适用于包含二进制数据的场景。

void *redisClusterCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen);

参数

  • cc:Redis 集群上下文

  • argc:参数个数

  • argv:参数数组,如 {"SET", "key", "value"}

  • argvlen:每个参数的长度数组

返回值

  • 返回 redisReply* 指针,需手动 freeReplyObject() 释放

⑦redisClusterFree

释放上下文资源,避免内存泄露。

void redisClusterFree(redisClusterContext *cc);
⑧redisClusterReset

重置上下文信息,清空连接和缓存,用于恢复连接失败后的重建。

void redisClusterReset(redisClusterContext *cc);
⑨redisClustervCommand
int redisClustervCommand(redisClusterContext *cc, void **reply, const char *format, va_list ap);
⑩redisClusterGetReply
int redisClusterGetReply(redisClusterContext *cc, void **reply);
⑪redisClustervAppendCommand
int redisClustervAppendCommand(redisClusterContext *cc, const char *format, va_list ap);
⑫redisClusterAppendCommandArgv
int redisClusterAppendCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen);

redisCluster连接的两种方式:

方式一:简洁连接(推荐,自动 connect)

redisClusterContext *cc = redisClusterConnect("127.0.0.1:7000", 0);

这个函数会:

  • 创建 redisClusterContext

  • 设置初始节点列表

  • 自动调用 redisClusterConnect2() 进行实际连接

所以:如果你用了 redisClusterConnect(),就不需要自己手动调用 redisClusterConnect2()

方式二:手动配置 + 显式连接

这种方式适用于你需要更精细控制连接参数的场景:

redisClusterContext *cc = redisClusterContextInit();  // 只创建上下文
redisClusterSetOptionAddNodes(cc, "127.0.0.1:7000");  // 添加节点
redisClusterSetOptionConnectTimeout(cc, timeout);     // 设置连接超时等选项
redisClusterConnect2(cc);                             // 必须手动调用连接

在使用 redisClusterContextInit() + 设置选项方式时,你必须手动调用 redisClusterConnect2(),否则上下文未建立连接,后续命令会失败。

 示例:

⑬异步相关api
函数原型功能说明函数参数返回值
redisClusterAsyncConnect(const char *startup_nodes, int flags)异步连接 Redis Cluster- startup_nodes:Redis Cluster 节点的启动地址列表,格式如 "127.0.0.1:7000"。 - flags:连接选项标志。返回 redisClusterAsyncContext*,表示异步上下文。
redisClusterAsyncSetConnectCallback(redisClusterAsyncContext *acc, redisConnectCallback *fn)设置连接成功的回调函数- acc:异步上下文。 - fn:连接成功回调函数,参数为 redisClusterAsyncContext* accredisContext* c无返回值
redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn)设置连接断开的回调函数- acc:异步上下文。 - fn:断开连接回调函数,参数为 redisClusterAsyncContext* accredisContext* c无返回值
redisClusterAsyncCommand(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, const char *format, ...)异步发送格式化命令- acc:异步上下文。 - fn:命令执行完后的回调函数,参数为 redisClusterAsyncContext* accredisReply* r。 - privdata:回调函数的用户数据。 - format:命令的格式化字符串。返回 void*,通常传入回调函数。
redisClusterAsyncCommandArgv(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen)异步参数数组方式发送命令- acc:异步上下文。 - fn:命令执行完后的回调函数,参数为 redisClusterAsyncContext* accredisReply* r。 - privdata:回调函数的用户数据。 - argc:参数数量。 - argv:命令的参数数组。 - argvlen:每个参数的长度。返回 void*,通常传入回调函数。
redisClusterLibeventAttach(redisClusterAsyncContext *acc, struct event_base *base)将异步 Redis Cluster 上下文绑定到 libevent- acc:异步上下文。 - base:libevent 事件循环的基础对象。返回 int,成功为 0,失败为 -1。
redisClusterAsyncFree(redisClusterAsyncContext *acc)释放异步上下文- acc:异步上下文。无返回值

示例:

#include <iostream>
#include <hiredis-vip/hircluster.h>
#include <event2/event.h>

// 回调函数:连接成功
void connectCallback(redisClusterAsyncContext *acc, redisContext *c) {
    std::cout << "Connected to Redis Cluster!" << std::endl;
}

// 回调函数:连接断开
void disconnectCallback(redisClusterAsyncContext *acc, redisContext *c) {
    std::cout << "Disconnected from Redis Cluster." << std::endl;
}

// 回调函数:命令响应
void commandCallback(redisClusterAsyncContext *acc, redisReply *r) {
    if (r != NULL) {
        std::cout << "Command reply: " << r->str << std::endl;
    } else {
        std::cout << "Command failed" << std::endl;
    }
}

// 主函数
int main() {
    // 初始化 libevent 事件循环
    struct event_base *base = event_base_new();
    if (!base) {
        std::cerr << "Could not initialize libevent!" << std::endl;
        return -1;
    }

    // 异步连接 Redis Cluster
    const char *startup_nodes = "127.0.0.1:7000,127.0.0.1:7001";
    redisClusterAsyncContext *acc = redisClusterAsyncConnect(startup_nodes, 0);
    if (acc == NULL || acc->err) {
        std::cerr << "Connection error: " << (acc ? acc->errstr : "NULL") << std::endl;
        event_base_free(base);
        return -1;
    }

    // 设置连接成功的回调
    redisClusterAsyncSetConnectCallback(acc, connectCallback);

    // 设置连接断开的回调
    redisClusterAsyncSetDisconnectCallback(acc, disconnectCallback);

    // 将异步上下文与 libevent 事件循环绑定
    if (redisClusterLibeventAttach(acc, base) != 0) {
        std::cerr << "Failed to attach to libevent!" << std::endl;
        redisClusterAsyncFree(acc);
        event_base_free(base);
        return -1;
    }

    // 异步发送 SET 命令
    redisClusterAsyncCommand(acc, commandCallback, NULL, "SET %s %s", "foo", "bar");

    // 异步发送 GET 命令
    redisClusterAsyncCommand(acc, commandCallback, NULL, "GET %s", "foo");

    // 启动事件循环
    event_base_dispatch(base);

    // 释放资源
    redisClusterAsyncFree(acc);
    event_base_free(base);

    return 0;
}


 

<libevent.h>

<hiredis-vip/adapters/libevent.h>是 hiredis-vip 提供的 Libevent 适配器模块,用于将异步 Redis/Redis Cluster 客户端集成进基于 libevent 的事件驱动程序中,实现非阻塞式通信。

  • hiredis-vip 支持 异步模式:你发命令出去,不会等 Redis 响应,你继续干别的事。响应来了,它会回调你,告诉你结果。

  • 但是这时候需要一个人帮你“看着 socket”,当有数据能读、能写了时,提醒你去处理一下 , 这个人就是 libevent

  • 所以你要把 hiredis-vip 的 Redis 客户端和 libevent 建立联系:你告诉 libevent:你帮我盯着 hiredis 的 socket,有事了通知我。

  • hiredis-vip/adapters/libevent.h 就是负责搭桥的,让这两者能配合起来完成这一切。

在高性能网络服务中,比如使用 libevent 写的 HTTP 服务或消息系统,你不想因为一个 Redis 命令就阻塞整个线程,你希望:

  • 我发一个 SET 命令 → Redis 那边处理它 → 回来后你再告诉我结果(回调函数)

  • 我整个服务的主线程仍然在跑,不停响应其他请求

  • 这种模式就必须用 非阻塞 I/O + 事件驱动 + 回调机制

这正是 libevent + hiredis-vip async 的组合实现的。

  • hiredis 提供了 redisAsyncContext:可以用它异步发送命令 + 设置回调处理响应。

  • 但它自己不处理 socket 的可读/可写通知,你要告诉一个 event loop 去“监听 socket fd”

  • libevent 是个 event loop 框架,它可以 event_add() 来监听 socket 的各种事件

  • 所以 redisLibeventAttach() 就是把两者连接起来,把 redisAsyncContext 的 socket 交给 libevent 管

  • 以后 Redis 响应来了,libevent 会帮你触发你之前设置的回调函数


API

redisLibeventAttach

将 Redis 上下文(无论是同步的 redisContext 还是异步的 redisAsyncContext)与 libevent 事件循环绑定,使得 Redis 能够使用 libevent 进行非阻塞的 I/O 操作。

int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base);
  • 参数

    • c:指向 异步Redis 上下文的指针=。

    • baselibevent 事件循环的基础对象,通常是通过 event_base_new() 创建。

  • 返回值

    • 成功时返回 0

    • 失败时返回 -1

示例:

#include <iostream>
#include <hiredis-vip/hiredis.h>
#include <hiredis-vip/adapters/libevent.h>
#include <event.h>

// 回调函数:处理 Redis 响应
void reply_callback(redisAsyncContext *c, void *r, void *privdata) {
    redisReply *reply = (redisReply *)r;
    if (reply == nullptr) {
        std::cerr << "Error: " << c->errstr << std::endl;
        return;
    }
    
    std::cout << "Redis reply: " << reply->str << std::endl;
}

int main() {
    // 1. 创建 libevent 事件基础对象
    struct event_base *base = event_base_new();
    if (!base) {
        std::cerr << "Error creating event base." << std::endl;
        return -1;
    }

    // 2. 创建异步 Redis 上下文并连接 Redis 服务器
    redisAsyncContext *ac = redisAsyncConnect("127.0.0.1", 6379);
    if (ac == nullptr || ac->err) {
        std::cerr << "Connection error: " << (ac ? ac->errstr : "Unknown error") << std::endl;
        return -1;
    }

    // 3. 将异步 Redis 上下文绑定到 libevent 事件循环
    if (redisLibeventAttach(ac, base) != 0) {
        std::cerr << "Error attaching Redis context to libevent." << std::endl;
        return -1;
    }

    // 4. 发送一个 Redis 命令并设置回调函数
    redisAsyncCommand(ac, reply_callback, nullptr, "SET %s %s", "key", "value");

    // 5. 启动事件循环,开始处理异步事件
    event_base_dispatch(base);

    // 6. 清理
    redisAsyncFree(ac);
    event_base_free(base);

    return 0;
}

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

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

相关文章

JavaScript 的编译与执行原理

文章目录 前言&#x1f9e0; 一、JavaScript 编译与执行过程1. 编译阶段&#xff08;发生在代码执行前&#xff09;✅ 1.1 词法分析&#xff08;Lexical Analysis&#xff09;✅ 1.2 语法分析&#xff08;Parsing&#xff09;✅ 1.3 语义分析与生成执行上下文 &#x1f9f0; 二…

NHANES指标推荐:FMI

文章题目&#xff1a;Exploring the relationship between fat mass index and metabolic syndrome among cancer patients in the U.S: An NHANES analysis DOI&#xff1a;10.1038/s41598-025-90792-9 中文标题&#xff1a;探索美国癌症患者脂肪量指数与代谢综合征之间的关系…

【JDBC】JDBC常见错误处理方法及驱动的加载

MySQL8中数据库连接的四个参数有两个发生了变化 String driver "com.mysql.cj.jdbc.Driver"; String url "jdbc:mysql://127.0.0.1:3306/mydb?useSSLfalse&useUnicodetrue&characterEncodingutf8&serverTimezoneAsia/Shanghai"; 或者Strin…

车载以太网驱动智能化:域控架构设计与开发实践

title: 车载以太网驱动专用车智能化&#xff1a;域控架构设计与开发实践 date: 2023-12-01 categories: 新能源汽车 tags: [车载以太网, 电子电气架构, 域控架构, 专用车智能化, SOME/IP, AUTOSAR] 引言&#xff1a;专用车智能化转型的挑战与机遇 专用车作为城市建设与工业运输…

如何利用技术手段提升小学数学练习效率

在日常辅导孩子数学作业的过程中&#xff0c;我发现了一款比较实用的练习题生成工具。这个工具的安装包仅1.8MB大小&#xff0c;但基本能满足小学阶段的数学练习需求。 主要功能特点&#xff1a; 参数化出题 可自由设置数字范围&#xff08;如10以内、100以内&#xff09; 支…

BGP路由策略 基础实验

要求: 1.使用Preva1策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2.用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3.配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4.使用Local Preference策略&#xff0c;确保R1通过R2到达192.168.1.0/24 …

第9讲、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架构的核心组件&#xff0c;也是现代深度学习中最重要的注意力机制之一。本文将从原理、实现和应用三个方面深入剖析这一机制。 1. 基本原理 Scaled Dot-Product Attention的本质是一种加权求和机制&#xff0c;通过计算查询(Query…

双向长短期记忆网络-BiLSTM

5月14日复盘 二、BiLSTM 1. 概述 双向长短期记忆网络&#xff08;Bi-directional Long Short-Term Memory&#xff0c;BiLSTM&#xff09;是一种扩展自长短期记忆网络&#xff08;LSTM&#xff09;的结构&#xff0c;旨在解决传统 LSTM 模型只能考虑到过去信息的问题。BiLST…

MySQL UPDATE 执行流程全解析

引言 当你在 MySQL 中执行一条 UPDATE 语句时&#xff0c;背后隐藏着一套精密的协作机制。从解析器到存储引擎&#xff0c;从锁管理到 WAL 日志&#xff0c;每个环节都直接影响数据一致性和性能。 本文将通过 Mermaid 流程图 和 时序图&#xff0c;完整还原 UPDATE 语句的执行…

亚马逊云科技:开启数字化转型的无限可能

在数字技术蓬勃发展的今天&#xff0c;云计算早已突破单纯技术工具的范畴&#xff0c;成为驱动企业创新、引领行业变革的核心力量。亚马逊云科技凭借前瞻性的战略布局与持续的技术深耕&#xff0c;在全球云计算领域树立起行业标杆&#xff0c;为企业和个人用户提供全方位、高品…

【实测有效】Edge浏览器打开部分pdf文件显示空白

问题现象 Edge浏览器打开部分pdf文件显示空白或显示异常。 ​​​​​​​ ​​​​​​​ ​​​​​​​ 问题原因 部分pdf文件与edge浏览器存在兼容性问题&#xff0c;打开显示异常。 解决办法 法1&#xff1a;修改edge配置 打开edge浏览器&#x…

RJ连接器的未来:它还会是网络连接的主流标准吗?

RJ连接器作为以太网接口的代表&#xff0c;自20世纪以来在计算机网络、通信设备、安防系统等领域中占据了核心地位。以RJ45为代表的RJ连接器&#xff0c;凭借其结构稳定、信号传输可靠、成本低廉等优势&#xff0c;在有线网络布线领域被广泛采用。然而&#xff0c;在无线网络不…

Redis持久化机制详解:保障数据安全的关键策略

在现代应用开发中&#xff0c;Redis作为高性能的内存数据库被广泛使用。然而&#xff0c;内存的易失性特性使得持久化成为Redis设计中的关键环节。本文将全面剖析Redis的持久化机制&#xff0c;包括RDB、AOF以及混合持久化模式&#xff0c;帮助开发者根据业务需求选择最适合的持…

DeepSeek 大模型部署全指南:常见问题、优化策略与实战解决方案

DeepSeek 作为当前最热门的开源大模型之一&#xff0c;其强大的语义理解和生成能力吸引了大量开发者和企业关注。然而在实际部署过程中&#xff0c;无论是本地运行还是云端服务&#xff0c;用户往往会遇到各种技术挑战。本文将全面剖析 DeepSeek 部署中的常见问题&#xff0c;提…

嵌入式培训之数据结构学习(五)栈与队列

一、栈 &#xff08;一&#xff09;栈的基本概念 1、栈的定义&#xff1a; 注&#xff1a;线性表中的栈在堆区&#xff08;因为是malloc来的&#xff09;&#xff1b;系统中的栈区存储局部变量、函数形参、函数返回值地址。 2、栈顶和栈底&#xff1a; 允许插入和删除的一端…

RabbitMQ--进阶篇

RabbitMQ 客户端整合Spring Boot 添加相关的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> 编写配置文件&#xff0c;配置RabbitMQ的服务信息 spri…

Android Studio报错Cannot parse result path string:

前言 最近在写个小Demo&#xff0c;参考郭霖的《第一行代码》&#xff0c;学习DrawerLayout和NavigationView&#xff0c;不知咋地&#xff0c;突然报错Cannot parse result path string:xxxxxxxxxxxxx 反正百度&#xff0c;问ai都找不到答案&#xff0c;报错信息是完全看不懂…

关于网站提交搜索引擎

发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由&#xff1a; 提高可见性&#xff1a;通过将您的网站提交给搜索引擎&#xff0c;可以提高您的网站在搜索结果中出现的机会。当用…

基于QT(C++)OOP 实现(界面)酒店预订与管理系统

酒店预订与管理系统 1 系统功能设计 酒店预订是旅游出行的重要环节&#xff0c;而酒店预订与管理系统中的管理与信息透明是酒店预订业务的关键问题所在&#xff0c;能够方便地查询酒店信息进行付款退款以及用户之间的交流对于酒店预订行业提高服务质量具有重要的意义。 针对…

机械元件杂散光难以把控?OAS 软件案例深度解析

机械元件的杂散光分析 简介 在光学系统设计与工程实践中&#xff0c;机械元件的杂散光问题对系统性能有着不容忽视的影响。杂散光会降低光学系统的信噪比、图像对比度&#xff0c;甚至导致系统功能失效。因此&#xff0c;准确分析机械元件杂散光并采取有效抑制措施&#xff0c…