hiredis 架构与功能分析
分析对象:hiredis v1.0.1(C 语言 Redis 客户端库,Redis 官方维护)
分析范围:hiredis 库本身的功能、依赖关系与架构设计
分析日期:2026-10-23
目录
一、概述
hiredis 是 Redis 官方维护的 C 语言客户端库,特点是极轻量、核心无第三方依赖,提供两套 API:
- 同步 API (
redisContext+redisCommand/redisGetReply+redisReply)------阻塞式,调用即返回结果 - 异步 API (
redisAsyncContext+ 回调 + 事件适配器adapters/*.h)------配合 libevent / libuv / Qt 等事件循环
代码总量约 7300 行(不含测试),只做「协议 + 传输」,不做连接池、集群、重试策略等上层能力。
二、功能分析
2.1 连接管理
| 能力 | 对应 API | 说明 |
|---|---|---|
| TCP 连接 | redisConnect / redisConnectWithTimeout |
指定 ip:port,可带连接超时 |
| Unix socket | redisConnectUnix |
本机域套接字 |
| 非阻塞连接 | redisConnectNonBlock / redisConnectBindNonBlock |
配合事件循环做异步建连 |
| 复用已有 fd | redisConnectFd |
接管一个已打开的 socket |
| 重连 | redisReconnect |
按保存的选项重连 |
| 超时 / KeepAlive | redisSetTimeout / redisEnableKeepAlive |
命令超时、TCP 保活 |
| 通用选项 | redisConnectWithOptions(redisOptions*) |
统一入口,含 bind 地址、超时、PUSH 回调等 |
2.2 命令发送与回复获取
- 同步 :
redisCommand(printf 风格)/redisCommandArgv(显式长度数组,二进制安全),内部等价于「redisAppendCommand+redisGetReply」 - 流水线 :
redisAppendCommand连续追加多条命令,最后统一redisGetReply逐条取回,减少 RTT - 异步 :
redisAsyncCommand发出命令并注册回调,回复到达时按顺序回调 - 回复对象 :统一返回
redisReply*,调用方负责freeReplyObject(异步模式由库代管)
2.3 RESP 协议解析
- 支持 RESP2 全部类型与 RESP3 新增类型:
STRING / ARRAY / INTEGER / NIL / STATUS / ERROR / DOUBLE / BOOL / MAP / SET / ATTR / PUSH / BIGNUM / VERB - 增量解析 :
redisReader独立于连接,半包数据可暂存续读 - 可插拔构造器 :
redisReaderCreateWithFunctions允许自定义回复对象,默认构造redisReply - 安全防护:缓冲上限 16KB、数组元素上限 2^32-1、整数/浮点严格解析防溢出
2.4 发布 / 订阅
- 频道订阅
SUBSCRIBE/ 模式订阅PSUBSCRIBE,退订UNSUBSCRIBE/PUNSUBSCRIBE - 异步层按「频道名 / 模式」路由到对应回调(
sub.channels/sub.patterns两个 dict) - 支持 RESP3 自发推送
PUSH的push_cb回调
2.5 功能总览
#mermaid-svg-EffjbnfOMS3rWqGk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EffjbnfOMS3rWqGk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EffjbnfOMS3rWqGk .error-icon{fill:#552222;}#mermaid-svg-EffjbnfOMS3rWqGk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EffjbnfOMS3rWqGk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EffjbnfOMS3rWqGk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EffjbnfOMS3rWqGk .marker.cross{stroke:#333333;}#mermaid-svg-EffjbnfOMS3rWqGk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EffjbnfOMS3rWqGk p{margin:0;}#mermaid-svg-EffjbnfOMS3rWqGk .edge{stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .section--1 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section--1 path,#mermaid-svg-EffjbnfOMS3rWqGk .section--1 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section--1 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section--1 text{fill:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth--1{stroke-width:17;}#mermaid-svg-EffjbnfOMS3rWqGk .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-0 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-0 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-0 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-0 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-0 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-0{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-0{stroke-width:14;}#mermaid-svg-EffjbnfOMS3rWqGk .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-1 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-1 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-1 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-1 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-1 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-1{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-1{stroke-width:11;}#mermaid-svg-EffjbnfOMS3rWqGk .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-2 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-2 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-2 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-2 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-2 text{fill:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-2{stroke-width:8;}#mermaid-svg-EffjbnfOMS3rWqGk .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-3 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-3 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-3 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-3 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-3 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-3{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-3{stroke-width:5;}#mermaid-svg-EffjbnfOMS3rWqGk .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-4 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-4 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-4 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-4 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-4 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-4{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-4{stroke-width:2;}#mermaid-svg-EffjbnfOMS3rWqGk .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-5 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-5 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-5 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-5 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-5 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-5{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-5{stroke-width:-1;}#mermaid-svg-EffjbnfOMS3rWqGk .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-6 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-6 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-6 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-6 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-6 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-6{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-6{stroke-width:-4;}#mermaid-svg-EffjbnfOMS3rWqGk .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-7 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-7 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-7 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-7 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-7 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-7{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-7{stroke-width:-7;}#mermaid-svg-EffjbnfOMS3rWqGk .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-8 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-8 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-8 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-8 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-8 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-8{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-8{stroke-width:-10;}#mermaid-svg-EffjbnfOMS3rWqGk .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-9 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-9 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-9 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-9 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-9 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-9{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-9{stroke-width:-13;}#mermaid-svg-EffjbnfOMS3rWqGk .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-10 rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-10 path,#mermaid-svg-EffjbnfOMS3rWqGk .section-10 circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-10 polygon,#mermaid-svg-EffjbnfOMS3rWqGk .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-10 text{fill:black;}#mermaid-svg-EffjbnfOMS3rWqGk .node-icon-10{font-size:40px;color:black;}#mermaid-svg-EffjbnfOMS3rWqGk .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .edge-depth-10{stroke-width:-16;}#mermaid-svg-EffjbnfOMS3rWqGk .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled,#mermaid-svg-EffjbnfOMS3rWqGk .disabled circle,#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:lightgray;}#mermaid-svg-EffjbnfOMS3rWqGk .disabled text{fill:#efefef;}#mermaid-svg-EffjbnfOMS3rWqGk .section-root rect,#mermaid-svg-EffjbnfOMS3rWqGk .section-root path,#mermaid-svg-EffjbnfOMS3rWqGk .section-root circle,#mermaid-svg-EffjbnfOMS3rWqGk .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-EffjbnfOMS3rWqGk .section-root text{fill:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .section-root span{color:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .section-2 span{color:#ffffff;}#mermaid-svg-EffjbnfOMS3rWqGk .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-EffjbnfOMS3rWqGk .edge{fill:none;}#mermaid-svg-EffjbnfOMS3rWqGk .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-EffjbnfOMS3rWqGk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} hiredis 功能
连接管理
TCP / Unix socket
阻塞 / 非阻塞
超时 / KeepAlive / 重连
复用已有 fd
命令与回复
同步 redisCommand
异步 redisAsyncCommand
流水线 AppendCommand
二进制安全 CommandArgv
协议解析
RESP2 / RESP3
增量半包续读
可插拔回复构造器
Pub/Sub
频道 / 模式订阅
回调路由
PUSH
RESP3 推送
基础支撑
sds 动态字符串
dict 字典
可替换分配器
SSL(可选)
三、依赖关系
3.1 hiredis 是否依赖 Qt
不依赖。hiredis 本身完全不依赖 Qt。
hiredis 是纯 C 库,核心源码只有 hiredis.c / read.c / net.c / sds.c 等,编译后就是 hiredis.dll + 头文件 hiredis.h,唯一的外部依赖是操作系统 socket 库(Windows 上是 ws2_32)。用它写一个 Redis 客户端不需要任何 Qt。
3.2 Qt 依赖点在何处
Qt 依赖只出现在 hiredis 的可选异步事件适配器中,按需使用:
| 层 | 是否依赖 Qt |
|---|---|
| hiredis 核心(同步 API) | ❌ 纯 C,无 Qt |
异步事件适配器 adapters/*.h |
⚠️ 按需------只有 qt.h 依赖 Qt,libevent.h/libev.h/libuv.h/glib.h 等各依赖各自的库,ae.h 零依赖 |
3.3 结论
- hiredis 库本身可以脱离 Qt 使用,同步 API 完全独立。
- 只有当使用它的 Qt 事件适配器(
adapters/qt.h,用于把异步 API 挂到 Qt 事件循环)时,才需要 Qt;改用 libevent/libev/libuv 适配器或纯同步 API 即可完全不依赖 Qt。
四、架构分析
4.1 总体定位
hiredis 是一个极简、分层的 C 语言 Redis 客户端,核心设计哲学:
- 只做协议和传输,不做连接池、集群、重试策略等上层能力
- 同步 / 异步双 API,共享同一套协议编解码内核
- 事件循环无关------异步层通过「事件适配器」插件化接入任意事件库
4.2 模块划分与文件职责
| 文件 | 行数 | 职责 |
|---|---|---|
hiredis.c |
1174 | 同步 API 核心:连接、命令发送、回复获取、释放 |
read.c |
739 | RESP 协议解析器(独立可复用的 redisReader) |
net.c |
612 | 网络连接层:TCP/Unix socket、阻塞/非阻塞、超时 |
async.c |
887 | 异步 API:回调管理、事件钩子、订阅分发 |
sds.c |
1289 | 动态字符串(Redis 同款 SDS,二进制安全) |
dict.c |
352 | 哈希字典(异步订阅的频道/模式路由用) |
alloc.c |
86 | 内存分配包装层,允许替换 allocator |
sockcompat.c |
248 | Windows socket 兼容层 |
ssl.c |
526 | SSL/TLS(可选) |
adapters/*.h |
--- | 8 种事件库适配器(ae/libev/libevent/libuv/glib/ivykis/macosx/qt) |
4.3 分层架构
#mermaid-svg-t1P8mC3Z4PK6CCo5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .error-icon{fill:#552222;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .marker.cross{stroke:#333333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 p{margin:0;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster-label text{fill:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster-label span{color:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster-label span p{background-color:transparent;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .label text,#mermaid-svg-t1P8mC3Z4PK6CCo5 span{fill:#333;color:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .node rect,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node circle,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node ellipse,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node polygon,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .rough-node .label text,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node .label text,#mermaid-svg-t1P8mC3Z4PK6CCo5 .image-shape .label,#mermaid-svg-t1P8mC3Z4PK6CCo5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .rough-node .label,#mermaid-svg-t1P8mC3Z4PK6CCo5 .node .label,#mermaid-svg-t1P8mC3Z4PK6CCo5 .image-shape .label,#mermaid-svg-t1P8mC3Z4PK6CCo5 .icon-shape .label{text-align:center;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .node.clickable{cursor:pointer;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .arrowheadPath{fill:#333333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t1P8mC3Z4PK6CCo5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t1P8mC3Z4PK6CCo5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster text{fill:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .cluster span{color:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t1P8mC3Z4PK6CCo5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .icon-shape,#mermaid-svg-t1P8mC3Z4PK6CCo5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .icon-shape p,#mermaid-svg-t1P8mC3Z4PK6CCo5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .icon-shape .label rect,#mermaid-svg-t1P8mC3Z4PK6CCo5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t1P8mC3Z4PK6CCo5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t1P8mC3Z4PK6CCo5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t1P8mC3Z4PK6CCo5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} API 层(hiredis.h / async.h)
调用方:业务代码
同步 API
redisConnect · redisCommand · redisGetReply
异步 API
redisAsyncConnect · redisAsyncCommand · 回调
命令格式化层
redisFormatCommand / redisFormatCommandArgv
协议解析层(read.c)
redisReader:RESP2/RESP3 → redisReply 树
传输层(net.c)
TCP/Unix socket · 阻塞/非阻塞 · 超时
基础支撑层
sds · dict · alloc · sockcompat
4.4 核心数据结构
#mermaid-svg-TYMkdUhhZsUNEVWY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TYMkdUhhZsUNEVWY .error-icon{fill:#552222;}#mermaid-svg-TYMkdUhhZsUNEVWY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TYMkdUhhZsUNEVWY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TYMkdUhhZsUNEVWY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TYMkdUhhZsUNEVWY .marker.cross{stroke:#333333;}#mermaid-svg-TYMkdUhhZsUNEVWY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TYMkdUhhZsUNEVWY p{margin:0;}#mermaid-svg-TYMkdUhhZsUNEVWY g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-TYMkdUhhZsUNEVWY g.classGroup text .title{font-weight:bolder;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster-label text{fill:#333;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster-label span{color:#333;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster-label span p{background-color:transparent;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster text{fill:#333;}#mermaid-svg-TYMkdUhhZsUNEVWY .cluster span{color:#333;}#mermaid-svg-TYMkdUhhZsUNEVWY .nodeLabel,#mermaid-svg-TYMkdUhhZsUNEVWY .edgeLabel{color:#131300;}#mermaid-svg-TYMkdUhhZsUNEVWY .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-TYMkdUhhZsUNEVWY .label text{fill:#131300;}#mermaid-svg-TYMkdUhhZsUNEVWY .labelBkg{background:#ECECFF;}#mermaid-svg-TYMkdUhhZsUNEVWY .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-TYMkdUhhZsUNEVWY .classTitle{font-weight:bolder;}#mermaid-svg-TYMkdUhhZsUNEVWY .node rect,#mermaid-svg-TYMkdUhhZsUNEVWY .node circle,#mermaid-svg-TYMkdUhhZsUNEVWY .node ellipse,#mermaid-svg-TYMkdUhhZsUNEVWY .node polygon,#mermaid-svg-TYMkdUhhZsUNEVWY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TYMkdUhhZsUNEVWY .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY g.clickable{cursor:pointer;}#mermaid-svg-TYMkdUhhZsUNEVWY g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-TYMkdUhhZsUNEVWY g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-TYMkdUhhZsUNEVWY .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-TYMkdUhhZsUNEVWY .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-TYMkdUhhZsUNEVWY .dashed-line{stroke-dasharray:3;}#mermaid-svg-TYMkdUhhZsUNEVWY .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-TYMkdUhhZsUNEVWY #compositionStart,#mermaid-svg-TYMkdUhhZsUNEVWY .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #compositionEnd,#mermaid-svg-TYMkdUhhZsUNEVWY .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #dependencyStart,#mermaid-svg-TYMkdUhhZsUNEVWY .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #dependencyStart,#mermaid-svg-TYMkdUhhZsUNEVWY .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #extensionStart,#mermaid-svg-TYMkdUhhZsUNEVWY .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #extensionEnd,#mermaid-svg-TYMkdUhhZsUNEVWY .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #aggregationStart,#mermaid-svg-TYMkdUhhZsUNEVWY .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #aggregationEnd,#mermaid-svg-TYMkdUhhZsUNEVWY .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #lollipopStart,#mermaid-svg-TYMkdUhhZsUNEVWY .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY #lollipopEnd,#mermaid-svg-TYMkdUhhZsUNEVWY .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-TYMkdUhhZsUNEVWY .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-TYMkdUhhZsUNEVWY .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TYMkdUhhZsUNEVWY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TYMkdUhhZsUNEVWY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TYMkdUhhZsUNEVWY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内嵌 reader
内嵌 c(复用同步底层)
构造
element\[\] 多叉树
redisContext
+funcs : redisContextFuncs
+err : int
+errstr : char128
+fd : redisFD
+flags : int
+obuf : char
+reader : redisReader
+push_cb : redisPushFn
redisReader
+buf : char
+pos : size_t
+len : size_t
+maxbuf : size_t
+task : redisReadTask*
+fn : redisReplyObjectFunctions
+reply : void
redisReply
+type : int
+integer : long long
+dval : double
+str : char
+len : size_t
+elements : size_t
+element : redisReply*
redisAsyncContext
+c : redisContext
+replies : redisCallbackList
+sub : channels / patterns
+onConnect
+onDisconnect
要点:
-
redisContext(同步连接句柄) ------hiredis.h:233-272funcs------ 函数表(redisContextFuncs),分层的关键:读写通过函数指针派发,普通连接与 SSL 连接共用上层代码err/errstr[128]------ 错误码与描述obuf------ 写缓冲;reader------ 指向redisReader协议解析器flags------ 位标志(REDIS_BLOCK阻塞 /REDIS_CONNECTED已连接 / ...)
-
redisReply(回复对象) ------hiredis.h:109-120- union 式结构,
type决定哪个字段有效 - 标量类型:
STRING / INTEGER / DOUBLE / NIL / STATUS / ERROR ARRAY用elements+element[]组成多叉树(嵌套数组递归表示)- v1.0.1 已支持 RESP3 新增类型:
MAP、SET、ATTR、PUSH、BIGNUM、VERB、BOOL
- union 式结构,
-
redisReader(协议解析器) ------read.h:95-113- 独立于连接、可单独复用的增量解析器
- 维护
buf/pos/len读缓冲游标(半包数据可续读) task栈 ------ 用任务栈而非递归来处理嵌套的 multi-bulk 回复redisReplyObjectFunctions fn------ 可插拔的回复对象构造器,默认构造redisReply,也可自定义maxbuf(默认 16KB)/maxelements------ 防护恶意超大回复
-
redisAsyncContext(异步句柄) ------async.h:63-112- 内嵌
redisContext c(复用全部同步底层) ev------ 事件钩子表(addRead/delRead/addWrite/delWrite/cleanup),由适配器填充,是异步层与事件循环的解耦点replies------ 普通命令回调链表;sub.channels/patterns------ 订阅回调字典onConnect/onDisconnect------ 连接生命周期回调
- 内嵌
4.5 同步命令工作流
Redis 服务器 传输层 (socket) 同步 API (redisContext) 调用方 Redis 服务器 传输层 (socket) 同步 API (redisContext) 调用方 #mermaid-svg-96PekYU9KBOQtfvP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-96PekYU9KBOQtfvP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-96PekYU9KBOQtfvP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-96PekYU9KBOQtfvP .error-icon{fill:#552222;}#mermaid-svg-96PekYU9KBOQtfvP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-96PekYU9KBOQtfvP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-96PekYU9KBOQtfvP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-96PekYU9KBOQtfvP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-96PekYU9KBOQtfvP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-96PekYU9KBOQtfvP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-96PekYU9KBOQtfvP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-96PekYU9KBOQtfvP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-96PekYU9KBOQtfvP .marker.cross{stroke:#333333;}#mermaid-svg-96PekYU9KBOQtfvP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-96PekYU9KBOQtfvP p{margin:0;}#mermaid-svg-96PekYU9KBOQtfvP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-96PekYU9KBOQtfvP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-96PekYU9KBOQtfvP .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-96PekYU9KBOQtfvP .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-96PekYU9KBOQtfvP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-96PekYU9KBOQtfvP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-96PekYU9KBOQtfvP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-96PekYU9KBOQtfvP .sequenceNumber{fill:white;}#mermaid-svg-96PekYU9KBOQtfvP #sequencenumber{fill:#333;}#mermaid-svg-96PekYU9KBOQtfvP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-96PekYU9KBOQtfvP .messageText{fill:#333;stroke:none;}#mermaid-svg-96PekYU9KBOQtfvP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-96PekYU9KBOQtfvP .labelText,#mermaid-svg-96PekYU9KBOQtfvP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-96PekYU9KBOQtfvP .loopText,#mermaid-svg-96PekYU9KBOQtfvP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-96PekYU9KBOQtfvP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-96PekYU9KBOQtfvP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-96PekYU9KBOQtfvP .noteText,#mermaid-svg-96PekYU9KBOQtfvP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-96PekYU9KBOQtfvP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-96PekYU9KBOQtfvP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-96PekYU9KBOQtfvP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-96PekYU9KBOQtfvP .actorPopupMenu{position:absolute;}#mermaid-svg-96PekYU9KBOQtfvP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-96PekYU9KBOQtfvP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-96PekYU9KBOQtfvP .actor-man circle,#mermaid-svg-96PekYU9KBOQtfvP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-96PekYU9KBOQtfvP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} redisCommand(c, "SET %s %b", key, val, len) redisvFormatCommand → RESP 命令字节流 redisAppendFormattedCommand 追加到 ctx->>obuf redisBufferWrite 冲刷 obuf 发送命令 RESP 回复字节 redisBufferRead 读入 reader->>buf redisReaderGetReply 解析出 redisReply 树 返回 redisReply*(调用方负责 freeReplyObject)
支持流水线 :redisAppendCommand 连续追加多条命令后统一 redisGetReply,一次往返发多条命令。
4.6 异步命令工作流
渲染错误: Mermaid 渲染失败: Parse error on line 17: ...c, reply, privdata) ----------------------^ Expecting 'SPACE', 'NEWLINE', 'INVALID', 'create', 'box', 'end', 'autonumber', 'activate', 'deactivate', 'title', 'legacy_title', 'acc_title', 'acc_descr', 'acc_descr_multiline_value', 'loop', 'rect', 'opt', 'alt', 'par', 'par_over', 'critical', 'break', 'participant', 'participant_actor', 'destroy', 'note', 'links', 'link', 'properties', 'details', 'ACTOR', got '1'
订阅消息则按 channels/patterns 字典路由到对应回调。
4.7 RESP 协议解析器------增量式状态机
RESP 类型字节表 ------read.c 的 processItem 先读 1 字节类型标记再分派:
| 字节 | 类型 | 说明 |
|---|---|---|
- / + |
ERROR / STATUS | 单行 |
: |
INTEGER | 单行整数 |
, |
DOUBLE | 单行浮点 |
_ |
NIL | 单行(空) |
# |
BOOL | RESP3 单行 t/f |
$ |
STRING | bulk 串 |
* |
ARRAY | 聚合 |
% / ~ |
MAP / SET | RESP3 聚合 |
= |
VERB | RESP3 verbatim 串 |
> |
PUSH | RESP3 推送 |
解析状态机
#mermaid-svg-0KflulbhqjXbFjL3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0KflulbhqjXbFjL3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0KflulbhqjXbFjL3 .error-icon{fill:#552222;}#mermaid-svg-0KflulbhqjXbFjL3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0KflulbhqjXbFjL3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0KflulbhqjXbFjL3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 .marker.cross{stroke:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0KflulbhqjXbFjL3 p{margin:0;}#mermaid-svg-0KflulbhqjXbFjL3 defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-0KflulbhqjXbFjL3 g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-0KflulbhqjXbFjL3 g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-0KflulbhqjXbFjL3 g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-0KflulbhqjXbFjL3 g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-0KflulbhqjXbFjL3 .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-0KflulbhqjXbFjL3 .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-0KflulbhqjXbFjL3 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-0KflulbhqjXbFjL3 .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-0KflulbhqjXbFjL3 .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-0KflulbhqjXbFjL3 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-0KflulbhqjXbFjL3 .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-0KflulbhqjXbFjL3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0KflulbhqjXbFjL3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0KflulbhqjXbFjL3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0KflulbhqjXbFjL3 .edgeLabel .label text{fill:#333;}#mermaid-svg-0KflulbhqjXbFjL3 .label div .edgeLabel{color:#333;}#mermaid-svg-0KflulbhqjXbFjL3 .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-0KflulbhqjXbFjL3 .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-0KflulbhqjXbFjL3 .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-0KflulbhqjXbFjL3 .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0KflulbhqjXbFjL3 .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0KflulbhqjXbFjL3 #statediagram-barbEnd{fill:#333333;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0KflulbhqjXbFjL3 .cluster-label,#mermaid-svg-0KflulbhqjXbFjL3 .nodeLabel{color:#131300;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-0KflulbhqjXbFjL3 .note-edge{stroke-dasharray:5;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-note text{fill:black;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram-note .nodeLabel{color:black;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagram .edgeLabel{color:red;}#mermaid-svg-0KflulbhqjXbFjL3 #dependencyStart,#mermaid-svg-0KflulbhqjXbFjL3 #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-0KflulbhqjXbFjL3 .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0KflulbhqjXbFjL3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} redisReaderGetReply(ridx==-1 初始化根任务)
读 1 字节类型标记
单行类型(- + : , _ # = >)
bulk 字符串($)
聚合(* % ~)
单行解析完成,moveToNextTask
声明长度 len
缓冲内 body 齐全
半包,等 redisReaderFeed 续喂
moveToNextTask
压入子任务栈
解析子元素
根任务完成,产出 redisReply 树
协议错误 / 长度越界 / OOM
Ready
ReadType
Line
Bulk
Aggregate
WaitBody
BulkDone
PushTask
Error
三类解析器
processLineItem------单行类型,readLine→seekNewline找\r\n;整数用string2ll严格解析防溢出processBulkItem------解析$len\r\n<data>\r\n,先确认 body 已在缓冲内 才消费;len == -1是空值processAggregateItem------解析*N,把子元素压入任务栈
核心机制:任务栈(而非递归)
嵌套数组不用函数递归,而用 task[] 栈:
redisReaderGetReply是驱动循环:ridx == -1时初始化task[0]为根,然后while (ridx >= 0) processItem(...)- 聚合类型把子任务压栈,
moveToNextTask在当前子元素用完时弹栈 - 栈默认 9 层 ,
redisReaderGrow每次 +9,支持任意深度嵌套
半包续读(增量解析的精髓)
缓冲是持久 sds + pos 游标 。每次检查 r->len - r->pos 是否够;不够就返回「数据不足」的软信号,redisReaderGetReply 返回 REDIS_OK 且 *reply == NULL,把半包留在缓冲里,等下次 redisReaderFeed 喂更多字节再继续。消费超过 1KB 时用 sdsrange 压缩缓冲,避免频繁 memmove。
安全防护
| 防护 | 位置 |
|---|---|
maxbuf 16KB:缓冲空且过大时重置 |
read.c |
maxelements 2^32-1:防超长数组声明 |
read.c |
| 长度越界检查 | read.c |
| 整数/浮点严格解析 + 溢出检查 | read.c |
| 出错清空缓冲、复位栈、设 errstr | read.c |
可插拔回复构造器
redisReaderCreateWithFunctions 接受 redisReplyObjectFunctions fn(read.h:85-93),提供 createString/createArray/createInteger/.../freeObject 六个钩子。fn == NULL 时 obj 存类型哨兵值,用于「只解析不构造对象」的场景。
4.8 异步订阅回调路由
回调分三路存放 (async.h:96-109)
| 存储 | 用途 |
|---|---|
replies(双向链表) |
普通命令回调,FIFO 按回复顺序 |
sub.channels(dict) |
频道订阅回调 |
sub.patterns(dict) |
模式订阅回调 |
sub.invalid(链表) |
已订阅状态下误发的普通命令 |
#mermaid-svg-xlyKyvqYVPWu0qcr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xlyKyvqYVPWu0qcr .error-icon{fill:#552222;}#mermaid-svg-xlyKyvqYVPWu0qcr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xlyKyvqYVPWu0qcr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xlyKyvqYVPWu0qcr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xlyKyvqYVPWu0qcr .marker.cross{stroke:#333333;}#mermaid-svg-xlyKyvqYVPWu0qcr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xlyKyvqYVPWu0qcr p{margin:0;}#mermaid-svg-xlyKyvqYVPWu0qcr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster-label text{fill:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster-label span{color:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster-label span p{background-color:transparent;}#mermaid-svg-xlyKyvqYVPWu0qcr .label text,#mermaid-svg-xlyKyvqYVPWu0qcr span{fill:#333;color:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr .node rect,#mermaid-svg-xlyKyvqYVPWu0qcr .node circle,#mermaid-svg-xlyKyvqYVPWu0qcr .node ellipse,#mermaid-svg-xlyKyvqYVPWu0qcr .node polygon,#mermaid-svg-xlyKyvqYVPWu0qcr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xlyKyvqYVPWu0qcr .rough-node .label text,#mermaid-svg-xlyKyvqYVPWu0qcr .node .label text,#mermaid-svg-xlyKyvqYVPWu0qcr .image-shape .label,#mermaid-svg-xlyKyvqYVPWu0qcr .icon-shape .label{text-anchor:middle;}#mermaid-svg-xlyKyvqYVPWu0qcr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xlyKyvqYVPWu0qcr .rough-node .label,#mermaid-svg-xlyKyvqYVPWu0qcr .node .label,#mermaid-svg-xlyKyvqYVPWu0qcr .image-shape .label,#mermaid-svg-xlyKyvqYVPWu0qcr .icon-shape .label{text-align:center;}#mermaid-svg-xlyKyvqYVPWu0qcr .node.clickable{cursor:pointer;}#mermaid-svg-xlyKyvqYVPWu0qcr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xlyKyvqYVPWu0qcr .arrowheadPath{fill:#333333;}#mermaid-svg-xlyKyvqYVPWu0qcr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xlyKyvqYVPWu0qcr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xlyKyvqYVPWu0qcr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xlyKyvqYVPWu0qcr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xlyKyvqYVPWu0qcr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xlyKyvqYVPWu0qcr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster text{fill:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr .cluster span{color:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xlyKyvqYVPWu0qcr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xlyKyvqYVPWu0qcr rect.text{fill:none;stroke-width:0;}#mermaid-svg-xlyKyvqYVPWu0qcr .icon-shape,#mermaid-svg-xlyKyvqYVPWu0qcr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xlyKyvqYVPWu0qcr .icon-shape p,#mermaid-svg-xlyKyvqYVPWu0qcr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xlyKyvqYVPWu0qcr .icon-shape .label rect,#mermaid-svg-xlyKyvqYVPWu0qcr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xlyKyvqYVPWu0qcr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xlyKyvqYVPWu0qcr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xlyKyvqYVPWu0qcr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} subscribe
unsubscribe
monitor / 其他
订阅态下误发
是
否,有普通回调
否,无普通回调
redisAsyncCommand 发出命令
nextArgument 反解首参,判断命令类型
置 REDIS_SUBSCRIBED
频道/模式插入 channels/patterns dict
不挂回调(退订逐频道回消息)
挂到 replies 链表
挂 sub.invalid
收到回复 redisProcessCallbacks
RESP3 PUSH?
交给 push_cb
从 replies 头取出,按序回调
订阅消息:看 element0 = message / pmessage
按频道名 dictFind 定位 channels / patterns 回调
订阅生命周期细节
pending_subs计数:同频道重复subscribe累加,收到确认时减 1- 退订:收到 unsubscribe 消息且
pending_subs == 0才dictDelete;全部清空时清除REDIS_SUBSCRIBED
回调内安全释放(防 use-after-free)
redisAsyncFree置REDIS_FREEING,若REDIS_IN_CALLBACK则延迟释放__redisRunCallback执行回调期间置/清REDIS_IN_CALLBACKredisProcessCallbacks在回调返回后检查REDIS_FREEING再真正释放
事件钩子与连接建立
ev钩子由适配器填充,宏_EL_ADD_READ/_EL_ADD_WRITE包装调用- 非阻塞 connect:首个读写事件时经
__redisAsyncHandleConnect检查连接完成,完成后onConnect(REDIS_OK)并置REDIS_CONNECTED __redisAsyncFree释放时:以 NULL reply 执行所有 pending 回调、通知事件库_EL_CLEANUP、调onDisconnect
4.9 关键设计点小结
- 函数表派发 (
hiredis.h:224-230):读写通过ctx->funcs->read/write间接调用,使ssl.c能「透明」包裹普通连接。 - 事件循环无关 :异步层靠
ev钩子 + 适配器头文件接入任意事件库,适配器都是 header-only。 - 二进制安全 :命令用
redisCommandArgv(显式长度数组),字符串用sds(内部记长度),全程不依赖\0。 - 可替换分配器 :
alloc.c把malloc/free抽象成可注入的函数指针。 - Windows 兼容 :
sockcompat.c+win32.h处理SOCKET类型差异、WSAStartup等。
两句话总结架构精髓:
- 解析器是「可暂停的增量状态机」:以「任务栈 + 持久缓冲游标」替代递归,以「数据不足返回软失败」实现半包续读。
- 异步层是「命令注册表 + 回复反查表」:命令发出时按首参预分类,回复回来时按「消息类型 + 频道名」反查回调,天然支持流水线与订阅交织。
五、总结
| 维度 | 结论 |
|---|---|
| 定位 | 极简、分层的 C 语言 Redis 客户端,只做协议 + 传输 |
| 功能 | 同步/异步命令、流水线、Pub/Sub、RESP2/RESP3 增量解析、PUSH 推送 |
| 依赖 | 核心纯 C 无第三方依赖;Qt 仅出现在可选异步适配器 qt.h 中 |
| 架构 | 双 API 共享同一编解码内核;解析器为「任务栈 + 增量缓冲」状态机;异步层「命令注册表 + 回复反查表」 |
文档结束。