【C++三方组件】redis-plus-plus:现代 Redis C++ 客户端
【摘要】:redis-plus-plus 在 hiredis 之上盖了一层现代 C++:std::string 与 Optional 的参数返回、异常错误通道、开箱即用的连接池、对象化的 pipeline 与订阅回调,哨兵与集群也内置。本篇实测连接池四线程并发写、pipeline 的类型化取回、发布订阅回调与异常体系;两个坑位:consume 一次只处理一条已到达的回复、订阅确认本身就是 SUBSCRIBE 的回复。末尾给出与 hiredis 的选型分界。
【关键词】:redis-plus-plus、Redis 客户端、连接池、pipeline、发布订阅
【版本基准】:redis-plus-plus 1.3.15(Apache-2.0)+ hiredis 1.4.1|示例 C++17|实测环境同第 36 篇(Redis 5.0.14.1 @ 127.0.0.1:6379)
1. What:hiredis 的一整层工程外衣
第 36 篇结尾列过 hiredis 的缺口:没有连接池、没有自动重连、reply 手动解析、线程不安全。redis-plus-plus(sewenew/redis-plus-plus)就是补齐这四件事的 C++ 封装------协议层仍是 hiredis,你在工程里穿的是这层外衣:
Redis对象:模板化的命令方法------redis.set(key, val)、redis.get(key)、redis.lpush(...),每个 Redis 命令一个同名方法 ,参数与返回值全是std::string/std::optional/ 整型;- 连接池 内置:
Redis对象线程安全地代理到池中连接; - 错误走异常 :
sw::redis::Error基类下分ReplyError、ConnectionError、TimeoutError等; - 哨兵 (Sentinel)与集群 (Cluster)模式内置,
ConnectionOptions一组字段切换。
支持 C++11/14/17,编译期选择;本篇示例按 C++17 用(Optional 返回正好衔接 std::optional)。
2. Why:裸 hiredis 之上的样板代码
用裸 hiredis 写应用,每个命令都是「格式串 + 类型分支 + freeReplyObject」三段式;GET 的 NIL 与 STRING 要分支处理;连接的线程模型要自己规划;断线检测与重连要自己写状态机。这些代码每个项目都要写一遍,而且写错的姿势基本相同 (忘了 free、判错顺序颠倒、重连后忘了重订阅)。redis-plus-plus 把这些样板收进库内:一行 redis.get(key) 背后是「借连接 → 发命令 → 解 reply → Optional 化 → 还连接」的完整路径。代价是引入一层模板编译时间与一个额外依赖(先装 hiredis),换来的是业务代码里没有一行协议细节。
3. How:接入与运行
- vcpkg :
vcpkg install redis-plus-plus(自动带 hiredis),CMake 走find_package(redis++ CONFIG REQUIRED),目标是redis++::redis++; - 源码 :先构建安装 hiredis,再构建安装 redis-plus-plus(它
find_package(hiredis)),共用一个安装前缀------示例工程的一键脚本与逐条命令见 README。
完整示例 redis_pp_demo.cpp:一个 main 串五节------基础读写、连接池并发、pipeline、发布订阅、异常体系。与第 36 篇共用同一台本机 Redis。
4. 基础:ConnectionOptions 与类型化读写
cpp
sw::redis::ConnectionOptions opts;
opts.host = "127.0.0.1";
opts.port = 6379;
opts.socket_timeout = std::chrono::milliseconds(500);
// opts.password = "..."; opts.db = 1; 按需
sw::redis::Redis redis(opts); // 单连接模式
redis.set("name", "redis-plus-plus");
if (auto v = redis.get("name")) // Optional<string>
// v->c_str() ...
if (!redis.get("missing"))
// Redis 的 NIL 就是空 Optional
对照第 36 篇:GET 的 NIL 分支从「判 r->type」变成 if (auto v = ...)------六种 reply 类型全部消失在类型系统里 。⚠️ socket_timeout / connect_timeout 建议永远显式设置:不设的话,网络故障时读操作可能无限期挂起,这是新库最常见的「偶现卡死」来源。
5. 连接池:线程安全从这里开始
cpp
sw::redis::ConnectionPoolOptions pool;
pool.size = 4;
sw::redis::Redis pooled(opts, pool); // 池化模式
std::thread t([&pooled] {
pooled.set("k", "v"); // 内部借还连接
});
池化与非池化的差别一句话:非池化的 Redis 对象一次只能跑一个操作(一条连接),池化的对象可以多线程并发调用,连接不够时等待归还。实测(4 线程 × 2500 条 SET,value 100 B,本机回环):
text
4 线程 x 2500 条:3528.2 ms(0.3 万条/秒)
这个数字与第 36 篇逐条往返同量级------瓶颈在每条命令一次网络往返,加线程只摊薄了等待、没消除往返。真正提速的钥匙还是 pipeline。
6. pipeline:类型化取回
cpp
auto pipe = pooled.pipeline();
auto replies = pipe.set("pipe:k", "v1")
.incr("pipe:cnt")
.incr("pipe:cnt")
.get("pipe:k")
.exec(); // 一次往返
replies.get<std::string>(0); // "OK"
replies.get<long long>(1); // 1
与第 36 篇的 redisAppendCommand / redisGetReply 对照:入队变成链式调用、取回变成按命令序号类型化 get ------freeReplyObject 与 type 分支都不存在了。⚠️ exec() 之前命令不会发出,忘了 exec 是新手的经典丢批量事故。
7. 发布订阅:回调注册 + consume 拉取
cpp
Subscriber sub = pooled.subscriber();
sub.on_message([](std::string channel, std::string msg) {
// 消息到达时被调用
});
sub.subscribe("pp:news");
sub.consume(); // 处理一条已到达的回复
实测输出与第 36 篇一致(收到 [pp:news] hello / world)。两个坑位要立牌:
consume()一次只处理一条已到达的回复 ------订阅确认占一条,每条消息各占一条;数错了次数,多调的那次会阻塞在没有消息的连接上;- 确认与消息都在同一个订阅连接上串行到达,发布侧用另一条连接(示例用池化对象 publish)。
生产代码里订阅通常配一个专门线程循环 consume(),把阻塞变成「等下一条消息」的正向语义------示例为了输出确定,用 100 ms 小睡等消息到齐再精确消费,这是测试写法不是生产写法。
8. 异常体系与选型
错误从返回值搬进异常,层次清晰:
cpp
try {
pooled.incr("non-number-key"); // 执行层
} catch (const sw::redis::ReplyError& e) { ... }
try {
sw::redis::Redis dead(bad_opts); // 传输层
} catch (const sw::redis::Error& e) { ... }
实测输出:ReplyError: ERR value is not an integer...、连接错误: failed to connect to Redis (127.0.0.1:16379): timed out...。sw::redis::Error 是公共基类------只想记日志不想分类时 catch 它一个就够。对照第 36 篇的双通道:hiredis 里「先判 nullptr 再判 type」的纪律,在这里变成两层 catch 的选择。
选型分界一句话:要 C 兼容、要最小依赖、要自己封装,选 hiredis(第 36 篇);要连接池、类型化 API、异常与哨兵集群开箱即用,选 redis-plus-plus(本篇)。还有一个工程判断:重度依赖模板的 redis-plus-plus 编译更慢、二进制更大,与它省下的样板代码相比通常划算------除非你的项目对构建时间极度敏感。
〔关联〕连接池的借还语义与线程安全边界,模式语言里是对象池模式的直接应用,见 cpp-design-patterns。
9. 参考资料
- 官方仓库 sewenew/redis-plus-plus:README 按命令字母序给全量方法签名,是最全的 API 参考;
- 连接池 / 哨兵 / 集群配置文档同在仓库 wiki;
- 完整构建与运行入口:examples/36-37/README(与第 36 篇共用);
- 上一篇(第 36 篇):hiredis------本篇脚下的协议底座。
参考 :sewenew/redis-plus-plus 1.3.15(Apache-2.0),底层 hiredis 1.4.1。本篇全部输出与吞吐(Optional 读、连接池并发、pipeline 取回、订阅回调、两类异常)均为本机实测(MSVC v145/VS2026,Windows;Redis 5.0.14.1 @ 127.0.0.1:6379)。