什么是 RESP
众所周知,Redis
是基于客户端-服务端模式工作的,客户端发送命令给服务端,服务端执行命令,然后将命令执行结果返回给客户端。为了满足Redis
高性能的要求,Redis
特地设计了RESP
(全称REdis Serialization Protocol
)协议,用来作为Redis
客户端与服务端的通讯协议,RESP
协议有以下优点
- 实现简单
- 解析高效
- 可读性好
注意:
RESP
底层用的连接方式还是TCP
,RESP
只定义了客户端与服务端的数据交互格式,这也是为什么Redis
能用Netcat
等工具进行操作的原因
初识 RESP
首先,我们使用redis-cli
执行PING
命令
$ redis-cli -h localhost -p 6379 PING
PONG
可以看到,Redis
服务端返回了PONG
。这是一条再简单不过的命令,接着我们使用Netcat
同样发送PING
命令给到Redis
服务端执行
$ echo "PING" | nc localhost 6379
+PONG
nc
即Netcat
是一款网络工具,可以很方便的从TCP
套接字中发送/读取数据,这里可以使用nc
与redis
交互是因为redis
底层连接使用的还是TCP
协议
可以看到,相比redis-cli
,Netcat
执行PING
命令的返回结果多了个+
号,这是为什么呢?
事实上,+PONG
才是redis-server
返回的真正结果,因为客户端与服务端之间进行数据交互的时候,都要遵循RESP
协议,RESP
协议规定,在每种数据类型的前面,都要增加一个额外的字节,用于区分不同的数据类型。比如用+
表示Simple Strings
类型。这也是为什么nc
的返回结果为+PONG
的原因,+
号表示返回结果是一个Simple Strings
类型,后面的PONG
则是Simple Strings
的内容
至于redis-cli
返回结果PONG
,则是redis-cli
解析后的结果
RESP 数据类型
在RESP
中,总共定义了5
种数据类型,分别是Simple String
、Errors
、Intergers
、Bulk Strings
和Arrays
,第一个字节与数据类型的映射关系如下:
- 对于
Simple Strings
类型,第一个字节为+
Errors
类型,第一个字节为-
Integers
类型,第一个字节为:
Bulk Strings
类型,第一个字节为$
Arrays
类型,第一个字节为*
RESP 简单字符串—Simple Strings
Simple Strings
的编码方式如下:
- 第一个字符为
+
- 紧接着是一个不能包含
CR
或LF
的字符串(不允许换行) - 以
CRLF
结束
CR
即\r
->回车,LF
即\n
->换行
Simple Strings
能保证在最小开销的情况下传输非二进制安全的字符串,比如很多Redis
命令执行成功都要返回OK
字符串,该字符串通过Simple Strings
编码为5
个字节的数据报如下
+OK\r\n
客户端在收到+OK\r\n
的时候,需要将其转换成客户端语言对应的字符串类型返回给调用者。字符串的值为+
号至\r\n
中间的字符串,也就是OK
如果需要返回二进制安全的字符串,则需要使用Bluk Strings
,后面会介绍
RESP 错误响应—Errors
Errors
是RESP
特定的数据类型,用于返回异常信息,Errors
的编码方式跟Simple Strings
的编码方式很像,唯一的区别是Errors
的第一个字节为-
,完整的的编码方式如下:
- 第一个字节为
-
- 紧接着是一个不能包含
CR
或LF
的字符串 - 以
CRLF
结束 只有在发生错误的时候才会返回Errors
,比如命令不存在/操作了错误的数据类型,Redis
客户端在收到Errors
类型的数据时,应该识别为异常消息,比如Redis
服务端返回的消息如下
-ERR unknown command `foo`\r\n
RESP 整型-Integer
Integer
的编码格式如下:
- 第一个字符为
:
- 紧接着为不能包含
CR
或LF
的数字 - 以
CRLF
结尾 在Redis
中,当需要返回整型的时候,就是用的Integer
进行返回,如INCR
、LLEN
等命令,比如返回1
:1\r\n
RESP Bluk Strings
Bluk Strings
用于表示二进制安全的字符串,最大为 512M(Bluk
有体积大的含义),Bluk Strings
的编码格式如下:
- 第一个字节为
$
- 紧接着一个数字用于表示字符串的字节数(称为
prefixed length
,也就是前缀长度),前缀长度以CRLF
结尾 - 接着为真实的字符串数据
- 以
CRLF
结尾 比如字符串foobar
使用Bluk Strings
编码为
$3\r\nfoobar\r\n
空字符串(对应java
中的""
)表示为
$0\r\n\r\n
特别注意的是,RESP
使用长度为-1
的Bluk Strings
表示不存在的值,即NULL
$-1\r\n
Redis
客户端在解析的时候,需要转换成对应语言的NULL
值,如Ruby
中的nil
,C
中的NULL
等
RESP 数组 Arrays
当服务端需要返回多个数据给客户端的时候,就是使用Arrays
进行返回的,如LRANGE
命令返回的就是一个数组。此外,客户端发送给服务端的命令,也都是使用Arrays
进行发送的。Arrays
的编码格式如下:
- 第一个字符为
*
- 后面接数组的元素个数,以
CRLF
结尾 - 接着为
Arrays
中每个元素的数据,元素类型为上面介绍的4
中数据类型
比如空数组可以表示为
*0\r\n
包含两个Bluk Strings
元素,元素内容分别为foo
跟bar
的Arrays
表示为
*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n
Arrays
中各个元素的数据类型可以不同,比如包含一个Integer
类型跟一个Simple Strings
类型的Arrays
,表示为
*2\r\n:1\r\n+hello\r\n
客户端发送命令也是使用Arrays
进行发送的,下面同样通过nc
举例,比如发送SET foo bar
命令
$ echo -e '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | nc localhost 6379
+OK
内联命令
Redis
提供了内联命令,方便我们在没有redis-cli
的时候,使用telnet
等其他工具与redis
进行交互,比如我们可以使用nc
与redis
交互
$ nc localhost 6379
PING
+PONG
SET foo bar
+OK
上面示例,PING
和SET foobar
是我们输入的命令,+PONG
和OK
则是服务端返回的结果
如果文章对你有帮助,可以搜索公众号huangxy
关注我,或扫下方二维码