RESP协议
- 实现简单.可以减低客户端出现bug的机率
- 解析速度快.由于RESP能知道返回数据的固定长度,所以不用像json那样扫描整个payload去解析, 所以它的性能是能跟解析二进制数据的性能相媲美的.
- 可读性好.
数据类型
- simple string. 简单的字符串
- error. 就是表示这是一个错误(异常)情况
- integer 表示这是一个整数
- bulk string. 表示是长字符串,但是必须小于512M.
- arrays. 表示这是一个数组,数组元素可以是上面的任意一种类型,也可以是一个数组
- simple string 的第一个字节是个”+”(加号), 后面接着的是字符串的内容, 最后以CRLF(\r\n)结尾.例如:
- error. error其实和string是类似的, 但是RESP为了能让不同客户端把这种error和正常的返回结果区分开来对待 (例如redis返回error的话,就抛出异常),特意多设计了这个数据类型. error类型的第一个字节是”-“(减号), 后面接着的是错误的信息, 最后以CRLF(\r\n)结尾,例如:
1
| "-ERR unknown command 'foobar'\r\n"
|
- integer 类型的第一个字节是”:”(冒号), 后面接着的是整数,最后以CRLF(\r\n)结尾, 例如:
- bulk string. 本质上也是字符串.跟普通字符串区分开来, 它的第一个字节是”$”(美元符号),紧接着是一个整数,表示字符串的字节数,字节数后面接一个CRLF. CRLF后面是字符串的内容, 最后以一个CRLF结尾. 例如:
1 2 3 4 5
| "$0\r\n" --$后面的0表示这是一个空字符串
"$-1\r\n" -- $后面的-1表示这是一个null字符串,Null Bulk String要求客户端返回空对象,而不能简单地返回个空字符串
"$6\r\nABCDEF\r\n" -- ABCDEF是6个字节,所以$后面是6
|
- arrays的第一个字节是”*”(星号), 紧接着后面是一个数字,表示这个数组的长度,数字后面是一个CRLF. 需要注意的是这个CRLF之后才是数组的真正内容, 而且数组内容可以是任意类型, 包括arrays和bulk string, 每个元素也要以CRLF结尾. 最后以CRLF(\r\n)结尾. 举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| "*0\r\n" --*后面的0表示表示空的数组
"*-1\r\n" --*后面的-1表示表示是null数组
"*5\r\n -- *5表示这是一个拥有5个元素的数组 +bar\r\n -- 第1个元素是简单的字符串 -unknown command\r\n -- 第2个元素是个异常 :3\r\n -- 第3个元素是个整数 $3\r\n -- 第4个元素是长度为3个字节的长字符串foo foo\r\n -- 第4个元素的内容 *3\r\n -- 第5个元素又是个数组 :1\r\n -- 第5个元素数组的第1元素 :2\r\n -- 第5个元素数组的第2元素 :3\r\n -- 第5个元素数组的第3元素 "
|
request-response模型
一般来说,redis客户端和服务端交互都是通过以下两个步骤:
1 2
| 1. redis发送一个命令到服务端, 然后阻塞在socket.read()方法, 等待服务端的返回 2. 服务端收到一个命令, 处理完成后将数据发送回去给客户端
|
这个就被称为request/reponse模型. redis的大部分命令都是使用这种模型进行通讯, 除了两种情况:
1 2
| 1. pipeline模式. 在pipeline模式下, 客户端可能会把多个命令收集在一起, 然后一并发送给服务端, 最后等待服务端把所有命令的执行响应一并发送回来 2. pub/sub, 发布订阅模式下, redis客户端只需要发送一次订阅命令
|
RESP协议的request/response模型可以总结为以下两个步骤
1 2
| 1. 客户端发送命令, 一般组装成bulk string的数组 2. 服务端处理命令, 根据不同的命令,可能返回不同的数据类型
|
例如命令”set test1 1” 一般被序列化成
1 2 3 4 5 6 7 8 9 10
| *3\r\n$3\r\nset\r\n$5\r\ntest1\r\n$1\r\n1\r\n
-- 为了方便理解, 每个CRLF我们给它换一下行 *3\r\n -- 这个命令包含3个(bulk)字符串 $3\r\n -- 第一个bulk string有3个字节 set\r\n -- 第一个bulk string是set $5\r\n -- 第二个bulk string有5个字节 test1\r\n -- 第二个bulk string是test1 $1\r\n -- 第三个bulk string有1个字节 1\r\n -- 第三个bulk string是1
|
它的返回是:
再例如命令”get test1”:
1 2 3 4 5 6 7 8
| *2\r\n$3\r\nget\r\n$5\r\ntest1\r\n 即: *2\r\n -- 这个命令是2个bulk字符串的数组 $3\r\n -- 第一个bulk字符串有3个字节: get get\r\n $5\r\n -- 第二个bulk字符串有5个字节: test1 test1\r\n
|
这个命令的返回是:
1 2
| $1\r\n -- 只有一个字节的bulk string 1\r\n
|
再来看一个错误的命令”get “, 这里我们get的命令故意不传参数
1 2 3 4 5 6 7 8 9
| request:
*1\r\n $3\r\n get\r\n
response(跟我们在redis-cli里面获取的提示是一样的):
-ERR wrong number of arguments for 'get' command\r\n
|