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