本篇文章针对的是
redis RESP2协议分析
整体分析
从整体角度来看,redis 客户端和服务端交互其实就两个:
- 客户端发送:
- 命令
- 缓存键,缓存值
- 服务端发送:
- 返回给客户端的结果
- 获取成功的
OK标志「字符串」 - 成功写入的元素个数
- 错误信息「
ERR」
从整体来讲,交互内容分为以下几个点:
- 命令:
Set, Get - 缓存键:
Set key value中的key,只能是字符串; - 缓存单个值:
Set key value中的value。字符串,数字,一般是这些; - 缓存集合:
HSet key value1 value2。缓存多个值的时候,传输的可能是不同类型的值; OK:命令执行成功,返回一个OK回复;Int:写入N个元素成功,返回N对应的int值ERR:错误信息
上面是命令的形式,下面我们讨论一下交互的数据:
首先 RESP 底层走的是 TCP ,最需要的考虑的问题之一就是粘包拆包,那么 redis server 如何区分出正确的包呢?通常3种方法:
- 数据包固定长度,不足不全,超过截断
- 分隔符区分,
HTTP就是用换行符区分的 - 数据包头部设置数据长度
自定义协议
redis 采取的是自定义协议格式区分不同命令请求,举个例子:
SET key value
=>
*3\r\n$3\r\nSET\r\n$9\r\nkey\r\n$6\r\nvalue\r\n
一时间看不懂这是啥,区分解释一下:
\r\n:分隔符*3:命令有3个参数$3...:当前参数的字符串长度
其实就是先区分发送字符边界,然后确定发送分送了多少字符串,然后是每一个字符串有多长【为什么是字符串,额。。。因为你发送的最终都是转换为字节流,至于区分什么数字集合那就用 type 区分就好】
最后这里列举一下响应协议内容:
+:单行/状态 回复-:错误回复::整数回复$:批量回复*:多条批量返回
接着我们来看看各种返回的情况:
单行返回
> SET foo tests
OK
----------
+OK\r\n
错误回复
> PUT foo tests
(error) ERR unknown command `PUT`
----------
-ERR unknown command `PUT`
整数回复
> EXISTS foo
(integer) 1
----------
:1\r\n
批量回复
> GET foo
"0123abcdefghigkl"
----------
$16\r\n0123abcdefghigkl\r\n
> GET foo2
(nil)
----------
$-1\r\n
多条批量回复
> LRANGE foo2 0 -1
1) "wxyz"
2) "qrstuv"
3) "lmnop"
------------
*3\r\n$4\r\nwxyz\r\n$6\r\nqrstuv\r\n$5\r\nlmnop\r\n
缺点
上述提到的是 5 种编码类型,从数据基本类型上只支持整数和字符串,但是对于其他基本数据类型:浮点数或者是布尔值【目前这两个是用 string/integer 来替换】。缺点还有以下这些:
lrange/zrange如果内容一样,返回的结果是一致的。但是其实存储的结构是不一样的,返回值对于开发者不明确;- 缺乏重要的基本数据类型;
- 返回的数据格式不是二进制安全的。因为分隔符为
\r\n,所以错误内容中不能含有这些信息
未完待续。。。