news 2026/5/7 14:45:31

Redis 通信协议 RESP 底层原理剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis 通信协议 RESP 底层原理剖析

前言

Redis 通信协议(RESP)是一种简单、高效、二进制安全的文本协议,核心是首字节标记类型 + 长度前缀 + CRLF 分隔,源码层面由网络 IO、协议解析、命令执行三部分协同完成。以下从协议规范、源码流程、核心函数与关键逻辑逐层解析。

核心设计思想

  • 二进制安全:长度前缀 + CRLF,无需转义,支持任意字节数据。
  • 解析高效:单线程循环解析,无锁,按长度直接读取,避免逐字符扫描。
  • 兼容简单:支持内联协议(如SET key val)与RESP 协议,自动识别。

原理

整体流程

Redis 基于单线程事件驱动 + I/O 多路复用(epoll/kqueue),请求处理链路:

客户端TCP连接 → acceptTcpHandler → 创建client结构体 → 注册读事件 → readQueryFromClient → processInputBuffer → 解析RESP → processCommand → 执行命令 → 序列化响应 → 写回客户端

源码解读

1. 连接接收(acceptTcpHandler)

// src/networking.c void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { // 1. 接受TCP连接,获取客户端fd int cfd = accept(fd, (struct sockaddr*)&sa, &salen); // 2. 创建client结构体(存储fd、querybuf、argv等) client *c = createClient(cfd); // 3. 注册读事件:fd可读时触发readQueryFromClient aeCreateFileEvent(el, cfd, AE_READABLE, readQueryFromClient, c); }

2. 数据读取(readQueryFromClient)

// src/networking.c void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { client *c = (client*)privdata; // 1. 扩展querybuf(SDS动态字符串,默认16KB) c->querybuf = sdsMakeRoomFor(c->querybuf, PROTO_IOBUF_LEN); // 2. 从socket读取数据到querybuf末尾 int nread = read(fd, c->querybuf+sdslen(c->querybuf), PROTO_IOBUF_LEN); // 3. 处理缓冲区:解析RESP/内联协议 processInputBuffer(c); }

关键querybuf是 SDS(动态字符串),自动扩容,累积客户端数据,避免分包问题。

3. 协议解析(processInputBuffer)

// src/networking.c void processInputBuffer(client *c) { while (sdslen(c->querybuf) > c->qb_pos) { // 1. 判断协议类型:首字节是* → RESP数组;否则内联协议 if (c->querybuf[c->qb_pos] == '*') { processMultibulkBuffer(c); // 解析RESP数组(主流客户端) } else { processInlineBuffer(c); // 解析内联协议(如telnet) } // 2. 解析完成:调用processCommand执行命令 if (c->argc > 0) { if (processCommand(c) == C_OK) { resetClient(c); // 重置状态,等待下一个命令 } } } // 3. 清理已处理数据 sdsrange(c->querybuf, c->qb_pos, -1); }

4. RESP 数组解析核心(processMultibulkBuffer)

客户端命令(如SET key val)序列化为 RESP 数组:*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\nval\r\n,解析逻辑:

// src/networking.c void processMultibulkBuffer(client *c) { char *p = c->querybuf + c->qb_pos; // 1. 读取数组元素个数:*后面的数字(如*3 → 3个元素) if (*p != '*') return; p++; c->multibulklen = strtol(p, &p, 10); // 解析数字 if (*p != '\r') return; p++; // 跳过\r if (*p != '\n') return; p++; // 跳过\n c->qb_pos = p - c->querybuf; // 2. 循环解析每个批量字符串元素 while (c->argc < c->multibulklen) { // 2.1 读取批量字符串长度:$后面的数字(如$3 → 3字节) if (*p != '$') return; p++; c->bulklen = strtol(p, &p, 10); if (*p != '\r') return; p++; if (*p != '\n') return; p++; c->qb_pos = p - c->querybuf; // 2.2 读取内容:长度为bulklen的字节 if (sdslen(c->querybuf) - c->qb_pos < c->bulklen + 2) return; c->argv[c->argc++] = sdsnewlen(p, c->bulklen); // 保存参数 p += c->bulklen + 2; // 跳过内容+\r\n c->qb_pos = p - c->querybuf; } }

5. 命令执行(processCommand)

校验。路由。执行Redis命令

根据解析好的 argv[0](命令名),在 Redis 全局命令表中匹配对应的命令结构体 redisCommand。

通过 cmd->proc(c) 执行真正的命令逻辑(setCommand、getCommand、delCommand 等)。

命令执行完毕后,统一封装 RESP 格式结果,写入客户端reply缓冲区,等待网络层发送给客户端。

// src/server.c int processCommand(client *c) { // 1. 查找命令:通过argv[0]在commandTable中匹配 struct redisCommand *cmd = lookupCommand(c->argv[0]); // 2. 执行命令:调用cmd->proc(如setCommand、getCommand) cmd->proc(c); // 3. 序列化响应:将结果转为RESP格式,写入client->reply return C_OK; }

6. 响应写回(sendReplyToClient)

  • 响应数据存入client->reply链表,由事件驱动触发写事件,调用sendReplyToClient序列化并写回客户端。
  • 示例:GET key返回$5\r\nhello\r\nSET key val返回+OK\r\n

7. 源码关键数据结构(src/server.h)

// 客户端结构体(核心) typedef struct client { int fd; // 客户端socket fd sds querybuf; // 输入缓冲区:存储客户端请求数据 sds *argv; // 命令参数数组(解析后) int argc; // 参数个数 list *reply; // 输出缓冲区:存储待发送响应 int multibulklen; // RESP数组元素个数 int bulklen; // 当前批量字符串长度 // ... 其他状态(认证、数据库、事务等) } client;

8. RESP2 vs RESP3(源码差异)

RESP2:5 种类型,无空值类型,用$-1\r\n表示 null,主流客户端默认。

RESP3:新增空值、布尔、浮点数、映射、集合等类型,支持客户端缓存,兼容 RESP2。

源码适配:processMultibulkBuffer扩展支持 RESP3 类型解析,client结构体新增标记位

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 14:44:30

WordPress AI内容生成插件:架构、配置与优化实战指南

1. 项目概述&#xff1a;一个为WordPress站点注入AI灵魂的文本生成插件如果你运营着一个WordPress网站&#xff0c;无论是个人博客、企业官网还是电商平台&#xff0c;内容创作永远是核心&#xff0c;也是最耗费精力的环节。每天绞尽脑汁想标题、写文章、更新产品描述&#xff…

作者头像 李华
网站建设 2026/5/7 14:40:00

从零构建CI/CD工作流:GitHub Actions实战与自动化设计精要

1. 项目概述&#xff1a;从零到一理解自动化工作流 最近在梳理团队内部的一些重复性开发与运维任务时&#xff0c;我再次深刻体会到&#xff0c;一个设计良好的自动化工作流&#xff0c;对于提升效率、减少人为错误、保证流程一致性有多么重要。这让我想起了之前在GitHub上关注…

作者头像 李华
网站建设 2026/5/7 14:35:31

炉石传说智能脚本完整指南:从零开始掌握自动化游戏技巧

炉石传说智能脚本完整指南&#xff1a;从零开始掌握自动化游戏技巧 【免费下载链接】Hearthstone-Script Hearthstone script&#xff08;炉石传说脚本&#xff09; 项目地址: https://gitcode.com/gh_mirrors/he/Hearthstone-Script 想要在《炉石传说》中实现高效自动化…

作者头像 李华
网站建设 2026/5/7 14:33:29

企业如何通过Taotoken实现多模型API的统一管理与审计

企业如何通过Taotoken实现多模型API的统一管理与审计 在构建基于大模型的应用时&#xff0c;中大型企业常面临一个现实挑战&#xff1a;多个内部项目团队可能各自对接不同的模型服务&#xff0c;导致API密钥分散、成本难以归集、调用行为不透明。这不仅带来安全风险&#xff0…

作者头像 李华
网站建设 2026/5/7 14:32:29

7-Zip-zstd终极指南:多算法压缩架构深度解析与实战优化

7-Zip-zstd终极指南&#xff1a;多算法压缩架构深度解析与实战优化 【免费下载链接】7-Zip-zstd 7-Zip with support for Brotli, Fast-LZMA2, Lizard, LZ4, LZ5 and Zstandard 项目地址: https://gitcode.com/gh_mirrors/7z/7-Zip-zstd 在数据爆炸式增长的时代&#xf…

作者头像 李华
网站建设 2026/5/7 14:29:52

为Node.js后端服务配置Taotoken实现稳定的大模型调用

为Node.js后端服务配置Taotoken实现稳定的大模型调用 1. 准备工作 在开始集成Taotoken服务之前&#xff0c;需要确保Node.js环境已准备就绪。推荐使用Node.js 16或更高版本&#xff0c;并确保已安装npm或yarn包管理器。创建一个新的项目目录或定位到现有后端项目&#xff0c;…

作者头像 李华