Linux下的c/c++开发之操作Redis数据库
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) | redisContext 、redisConnect() 、redisCommand() | 单个 Redis 实例(非集群) | 若用单机模式需要 |
<hiredis-vip/hircluster.h> | Redis Cluster 操作的核心接口 | redisClusterContext 、redisClusterCommand() | 分布式 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")// - 其他类型该字段为 NULLsize_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);
示例:
#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;
}
⑦异步相关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* acc 和 redisContext* c 。 | 无返回值 |
redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn) | 设置连接断开的回调函数 | - acc :异步上下文。 - fn :断开连接回调函数,参数为 redisClusterAsyncContext* acc 和 redisContext* c 。 | 无返回值 |
redisClusterAsyncCommand(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, const char *format, ...) | 异步发送格式化命令 | - acc :异步上下文。 - fn :命令执行完后的回调函数,参数为 redisClusterAsyncContext* acc 和 redisReply* r 。 - privdata :回调函数的用户数据。 - format :命令的格式化字符串。 | 返回 void* ,通常传入回调函数。 |
redisClusterAsyncCommandArgv(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) | 异步参数数组方式发送命令 | - acc :异步上下文。 - fn :命令执行完后的回调函数,参数为 redisClusterAsyncContext* acc 和 redisReply* 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 <event.h>
#include <cstring>// 连接成功的回调
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 *reply) {if (reply->type == REDIS_REPLY_STRING) {std::cout << "Reply: " << reply->str << std::endl;} else if (reply->type == REDIS_REPLY_STATUS) {std::cout << "Status: " << reply->str << std::endl;} else {std::cout << "Received unexpected reply type" << std::endl;}freeReplyObject(reply);
}int main() {// 创建 libevent 事件基础对象struct event_base *base = event_base_new();if (!base) {std::cerr << "Unable to create event base!" << std::endl;return -1;}// 异步连接 Redis ClusterredisClusterAsyncContext *acc = redisClusterAsyncConnect("127.0.0.1:7000", 0);if (acc == NULL || acc->err) {std::cerr << "Connection error: " << (acc ? acc->errstr : "NULL") << std::endl;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");// 发送带有参数数组的命令(例如 MSET)const char *argv[] = {"MSET", "foo", "bar", "baz", "qux"};size_t argvlen[] = {4, 3, 3, 3, 3};redisClusterAsyncCommandArgv(acc, commandCallback, NULL, 5, argv, argvlen);// 运行事件循环,等待命令执行event_base_dispatch(base);// 释放资源redisClusterAsyncFree(acc);event_base_free(base);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->err
和cc->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 集群上下文 -
tv
:struct 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()
函数,它会:
-
自动对 key 做哈希并映射到 slot
-
根据 slot,查找当前负责这个 slot 的节点
-
自动建立连接(或复用已有连接)并将命令发送给这个节点
-
返回对应的
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);
redis连接的两种方式:
方式一:简洁连接(推荐,自动 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* acc 和 redisContext* c 。 | 无返回值 |
redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn) | 设置连接断开的回调函数 | - acc :异步上下文。 - fn :断开连接回调函数,参数为 redisClusterAsyncContext* acc 和 redisContext* c 。 | 无返回值 |
redisClusterAsyncCommand(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, const char *format, ...) | 异步发送格式化命令 | - acc :异步上下文。 - fn :命令执行完后的回调函数,参数为 redisClusterAsyncContext* acc 和 redisReply* r 。 - privdata :回调函数的用户数据。 - format :命令的格式化字符串。 | 返回 void* ,通常传入回调函数。 |
redisClusterAsyncCommandArgv(redisClusterAsyncContext *acc, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) | 异步参数数组方式发送命令 | - acc :异步上下文。 - fn :命令执行完后的回调函数,参数为 redisClusterAsyncContext* acc 和 redisReply* 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 Clusterconst 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 上下文的指针=。 -
base
:libevent
事件循环的基础对象,通常是通过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;
}