搞懂Redis协议RESP
RESP (REdis Serialization Protocal)
Redis客户端和服务端之间通信的协议。它很简单,建立在TCP协议上,提供简单、高性能、可读性强的数据序列化的规范和语义。
5种数据模型
- Simple Strings
- Errors
- Integers
- Bulk Strings
- Array
Simple Strings
Simple Strings用于服务端对一些客户端命令的响应
格式:"+"开头 "\r\n"结尾,中间内容不能含有'\r'或'\n'
# set命令得到响应
127.0.0.1:6379> set name Foo
OK
# 服务端返回完整内容:"+OK\r\n“
# 退出命令
127.0.0.1:6379> quit
# 服务端返回的完整内容:+OK\r\n
Simple Strings的结构是非常简单的,而且不是二进制安全的;所以只会在服务端的简单内容返回。只是GET
、MGET
等命令的内容返回会用Buik Strings。
Errors
Errors 用于服务端对客户端一些错误命令的返回内容
格式:"-"开头 "\r\n"结尾
# 执行不存在的命令
127.0.0.1:6379> gee name
(error) ERR unknown command 'gee'
# 服务端返回完整内容:"-ERR unknown command 'gee'\r\n"
# 错误的类型操作
127.0.0.1:6379> set name Bob
OK
127.0.0.1:6379> zadd name 1 Foo
(error) WRONGTYPE Operation against a key holding the wrong kind of value
# 服务端返回完成内容:"-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
从例子中可以看出Erorrs响应中间部分的内容由两部分构成:大写代表错误类型,消息的表示错误详情。即-Error message\r\n
Integers
用于需要返回整数内容的命令,比如:INCR
、LLEN
等
格式:":"开头,\r\n结尾,中间部分必须是有效的整数
# incr
127.0.0.1:6379> set count 1
OK
127.0.0.1:6379> incr count
(integer) 2
# 服务端返回完整内容:":2\r\n"
# lpush llen del
127.0.0.1:6379> lpush userList Bob
(integer) 1
127.0.0.1:6379> llen userList
(integer) 1
# 服务端返回完整内容:":1\r\n",代表列表中当前元素书,数量
# del
127.0.0.1:6379> del userList
(integer) 1
# 服务端返回完整内容:":1\r\n", 含义是执行成功
# exists
127.0.0.1:6379> set name Bob
OK
127.0.0.1:6379> exists name2
(integer) 0
# 服务端返回完整内容:":0\r\n",含义是不存在
127.0.0.1:6379> exists name
(integer) 1
# 服务端返回完整内容:":1\r\n",含义是存在
这些命令的响应内容都是使用Integers:SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD
Bulk Strings
用来传输二进制安全的字符内容,最大长度是512MB
格式:'$'开头,后跟整数代表真正字符长度,然后\r\n,后面接真正的字符串内容,最后\r\n结尾
,例:"$6\r\nfoobar\r\n"
正常返回
127.0.0.1:6379> set name foo
OK
127.0.0.1:6379> get name
"foo"
# 服务端返回完整内容:"$3\r\nfoo\r\n"
空字符串返回
127.0.0.1:6379> set name ""
OK
127.0.0.1:6379> get name
""
# 服务端返回完整内容:"$0\r\n\r\n"
null返回,即get不存在的key
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
# 服务端返回完整内容:"$-1\r\n"
当服务器返回Null Bulk Strings时,客户端库应该转换为Null对象返回,而不要返回空字符。空字符串和nil是两个含义,空字符串代表键的值为'',nil代表键不存在。
Arrays
用作客户端向服务端发送命令时的内容格式,也用于服务端对一些命令的内容格式,如:LRANGE
格式:"*"开头,后跟整数表示数组长度,然后\r\n,后面是数组中的每个元素,他们可以是任意RESP类型(上面提到的Intergers、Bulk String等,格式如上),他们之间用\r\n分割,结尾\r\n
来看看set name Foo
这条命令,client是怎么传给server的
127.0.0.1:6379> set name Foo
OK
# 客户端发的完整内容"*3\r\n$3\r\nset\r\n$4\r\nname\r\n$3\r\nFoo\r\n"
*3
代表这个内容是Arrays类型,并且长度为3,因为set name Foo
有3个字符串\r\n
分割符$3
第一个字符串长度为3\r\n
分割符set
具体内容set\r\n
分割符$4
第二个字符串长度为4name
具体内容name\r\n
分割符$3
第三个字符串长度为3\r\n
分割符Foo
具体内容Foo\r\n
结尾
接着看看mget name name1
这条命令发出后,server返回的内容
127.0.0.1:6379> mget name name2
1) "Foo"
2) (nil)
# 服务端返回完成内容:*2\r\n$3\r\nFoo\r\n$-1\r\n"
*2
代表这个内容是Arrays类型,并且长度为2,因为我们查了两个键name
和name2
\r\n
$3
第一个字符串长度为3\r\n
Foo
具体内容Foo,即刚才set name Foo
命令生效结果\r\n
$-1
第二个字符串长度为-1,表示nil,不存在\r\n
null数组表示
127.0.0.1:6379> blpop foo 1
(nil)
# 服务端返回完成内容: "*-1\r\n"
同样,客户端库也要区别null数组和空数组的区别。
总结
RESP协议的5种数据使用场景