我们之前说了Redis服务器端的机制和关键技术,很少涉及到客户端的问题,但是Redis采用的是典型的client-server架构,客户端发起请求,服务器端返回相应给客户端
而其中Client和Server的交互,利用的是Redis自定义的RESP协议
早期版本是RESP 2协议,从6.0开始,使用RESP3 协议了,关于两者的区别,就是我们本章的说明重点
首先是RESP 2协议
如何对命令和数据进行格式编码的,分为客户端请求和服务器端响应流程
客户端会在给Redis发送命令的时候,带上要写入的键和值
服务器端响应的时候,Redis会读取返回的值,OK的标识,写入的元素个数,错误信息,以及命令
RESP2 将这些命令分为7类,分别是
命令:不同数据类型的操作命令,String类型的SET GET HSET HGET等
键:键值对中的键,用字符串表示
单个值:对应String类型的数据,数据本身可以是字符串 数值,布尔值
集合值:对应List Hash Set Sorted Set类型的数据,包含多个值
OK回复
整数回复
错误信息
根据这些返回类型,我们看下RESP2 的实现方式
RESP 2协议的目标是,希望Redis实现客户端更加方便,可以减少客户端开发时候出现的bug,所以直接使用可读性的文本形式进行编码
并且将上面命令分为了5种编码格式类型,区分这5种编码类型,RESP2 使用一个专门的字符串,表示每种编码类型的开头字符,这样,客户端或者服务器端在编码后的数据进行解析的时候,可以通过开头字符知道当前解析的编码类型
RESP在编码之后,根据单个命令或者单个数据的粒度进行编码,并且在每个编码的结果后面增加一个换行符 “\r\n”,表示编码的结束
RESP2的第一种编码格式
简单字符串类型
就是一个字符串进行编码前面用一个+表示是字符串,比如下面的编码格式
+OK\r\n
长字符串类型
对于长字符串类型,RESP2在前面使用$作为开头字符串,后面跟着一个数字表示字符串的实际长度
假设如下我们使用GET命令读取一个键,返回的String类型就会如下
$9 testvalue\r\n
整数类型
整数类型以 “:”字符作为开头字符,可以用于对服务端返回的整数回复进行编码
:3\r\n
错误类型
是一个字符串,包含了错误类型和具体的错误信息,RESP2使用 – 字符作为其开头字符
我们使用redis-cli执行PUT testkey testvalue的话
会得到一个ERROR信息,结果如下
-ERR unknown command
数组类型
一个包含多个元素的数组,元素类型可以是刚刚说的4种编码类型
客户端发送请求和服务器端返回结果的时候,数组类型都能用得上,其开头为 “*”字符
数据类型如下
*2\r\n$3\r\nGET\r\n$7\r\ntestkey\r\n
整体总结如下
虽然RESP 2提供了5种编码类型,但是在Redis中,还有很多额外的基本数据类型,比如浮点数,布尔值等
另一方面,RESP2只能区分字符串和整数,对于其他的数据类型,需要由客户端进行额外的转换操作,当需要一个浮点数的时候,就需要比较返回的字符串,是否可以转换为浮点数
而且RESP2使用数组表示所有的集合类型,但是集合包含了List Hash Set Sorted Set,当收到数组类型编码的时候,还需要判断是哪种集合类型
就好比,我们有两个集合操作,分别是HGETALL testhash 和 ZRANGE testzset 0 3 withscores
获取结果都是如下
127.0.0.1:6379>HGETALL testhash
1) “a” 2) “1” 3) “b” 4) “2” 5) “c” 6) “3” 127.0.0.1:6379>ZRANGE testzset 0 3 withscores 1) “a” 2) “1” 3) “b” 4) “2” 5) “c” 6) “3” |
那么就需要客户端根据这两者的请求命令操作,转换为对应的Hash和有序集合,增加了客户端额外的开销
而RESP3则是增加了多种数据类型的支持,包含空值,浮点数,布尔值,有序的字典集合,无序的集合等
这样客户端就不需要进行额外的命令获取比较了,提升了客户端的效率
总结一下这一节,我们说了RESP这个Redis自定义的通信协议,定义了Redis客户端和服务器端进行交互的格式,RESP2是之前版本的通信协议,定义了基本的5种编码格式,包含了字符串 长字符串,整数,错误类型,数组类型,每种类型都有自己独特的编码字符表示
不过RESP2的支持类型还是太少,于是Redis 6.0支持了RESP3协议,增加了对浮点数,布尔类型 有序字典集合,无序集合等多种类型的支持,但因为两者协议不兼容,对于协议不兼容需要注意
最后一个小问题,假如Redis实例中由一个List类型的数据,key为mylist,value为LPUSH命令写入List集合的5个元素,分别是1,2,3.3,4,hello,执行LRANGE mylist 0 4 命令的时候,返回给客户端的编码结果是怎么样的
应该如下
Trying 127.0.0.1…
Connected to localhost. Escape character is ‘^]’. LRANGE mylist 0 4 *5 $5 hello $1 4 $3 3.3 $1 2 $1 1 |
换行符已经交由命令行工具自动解析到了