几乎所有主流编程语言都提供了与 REDIS 服务端交互的 cli 平台,基于 TCP 构建的 通讯协议 RESP…
本文我们从 redis-cli 原生客户端的角度进行剖析 Redis 客户端功能流程
以及 RESP 协议
另外,由于 redis-cli 使用了 linenoise库
( linenoise 是一个命令行编辑库,由 Redis 设计者 Antirez 发布的一个用于替代 readline 的工具库,广泛应用于 Redis、MongoDB、Android等 ),而其中有 tty 限制 不能直接使用 gdb/cgdb 进行调试,建议使用 打印调试数据至日志文件 的方式。
当然,也可以通过 固定 src/redis-cli.c: line 1445
变量值的方式跳过 linenoise 函数,再使用 gdb/cgdb 进行调试
redis-cli 功能流程
redis-cli 作为最直观的 Redis交互客户端,功能流程主要如下
- 初始化默认参数配置 config
- 终端输出判断
- 参数解析
- config 赋值
- 判断 help 相关参数打印 usage 信息
- 判断 version 相关参数打印 version 信息,版本号 是根据 git 版本信息 而来的
- 输出模式判断
- Latency 延迟监控模式
--latency
- Latency 延迟图形监控模式
--latency-dist
- 从库模式
--slave
- RDB 数据转换模式
--rdb
- 管道/批量操作模式
--pipe
- 查找占用大内存的 key 模式
--bigkeys
- 查找热点 key
--hotkeys
- 统计模式
--stat
- 扫描执行模式 key
--scan
/--pattern
- LRU test 模式
--lru-test
- 测试延迟模式
--intrinsic-latency
- LUA 脚本模式
--eval
- Latency 延迟监控模式
- 连接服务端
- 命令交互
- 命令识别初始化
- linenoise 命令行模式设置、命令提示回调函数、命令补全回调函数、历史命令加载以及命令保存
- 命令行内容读取
- 命令行解析
- 识别特殊命令 quit / exit / restart / connect / clear 处理
- 命令协议格式转换
- 命令执行器,执行指定命令
- 执行时间记录,时间大于 500毫秒 则打印出来
redis-cli 参数详情如下,注释比较详细:
RESP 协议
为了保证 客户端与服务端 TCP 数据交互的一致性,同时也为了 Redis 的广泛推广,在 1.2 版本中引入了 RESP 协议( REdis Serialization Protocol ), 并最终在 Redis 2.0 版本成为 Redis 服务器通信的标准方式
;这一协议的产生,使得 各种编程语言 可以极为方便的对 Redis 进行数据操作。
RESP 协议在保证 TCP 操作一致性的同时,还 具备了 易实现、快速解析、可读性强
等特性,当然 协议格式的特殊性中,保证了数据是二进制安全的。
Request
请求协议格式如下,命令本身也是作为参数的,比如 GET、SET 等,作为 第一个 参数
比如 SET foo tests
,分解转换就是
最终传输内容主体就是
Response
响应协议内容一般根据不同数据类型分作多种情况进行返回,一般根据响应内容的第一个字节,确认返回类型:
单行/状态 回复
一般为 +
开头 \r\n
结尾 的单行字符串,比如
错误回复
错误回复的响应内容跟单行比较相像,区别就是 第一个字节的标识符 为 -
整数回复
整数回复也比较简单,标识符为 :
返回整数结果的命令有这些:SETNX 、 DEL 、 EXISTS 、 INCR 、 INCRBY 、 DECR 、 DECRBY 、 DBSIZE 、 LASTSAVE 、 RENAMENX 、 MOVE 、 LLEN 、 SADD 、 SREM 、 SISMEMBER 、 SCARD 等
批量回复
即为 多行字符串回复,字符串的最大长度为 512 MB ,协议模式为:$[字符串长度]\r\n[字符串内容]\r\n
,当然我们平时也会碰到 客户端提示 nil
,这样的协议内容格式为:$-1\r\n
多条批量回复
很多类似 LRANGE、SMEMBERS 等的命令都会返回多个值,对应协议为:
- 返回 多个数据(假如返回4个结果):
*[结果个数]\r\n$[结果1长度]\r\n[结果1内容]\r\n$[结果2长度]\r\n[结果2内容]\r\n$[结果3长度]\r\n[结果3内容]\r\n$[结果4长度]\r\n[结果4内容]\r\n
- 返回 空表:
*0\r\n
- 返回 nil(比如 BLPOP 命令)
*-1\r\n
123456789101112131415161718[wettper@web-lovers redis-src]$ src/redis-cli127.0.0.1:6379> LPUSH foo2 a bc def higk lmnop qrstuv wxyz(integer) 7------------实际返回解析前的内容::7\r\n127.0.0.1:6379> LRANGE foo2 0 -11) "wxyz"2) "qrstuv"3) "lmnop"4) "higk"5) "def"6) "bc"7) "a"------------实际返回解析前的内容:*7\r\n$4\r\nwxyz\r\n$6\r\nqrstuv\r\n$5\r\nlmnop\r\n$4\r\nhigk\r\n$3\r\ndef\r\n$2\r\nbc\r\n$1\r\na\r\n
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
主函数
普通操作交互模式主逻辑函数 repl()
命令执行函数 issueCommandRepeat()
,其中重要的是 发送命令函数 cliSendCommand()
按照协议格式发送内容给服务端,并获取响应,cliReadReply()
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-cli.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!