网络协议之:redis protocol 详解( 二 )

RESP protocolRESP协议有5种类型,分别是imple Strings, Errors, Integers, Bulk Strings 和 Arrays 。
不同的类型以消息中的第一个byte进行区分 , 如下所示:
类型第一个byteSimple Strings+Errors-Integers:Bulk Strings$Arrays*protocol中不同的部分以 "\r\n" (CRLF)来进行区别 。
Simple StringsSimple Strings的意思是简单的字符串 。
通常用在服务器端的返回中,这种消息的格式就是"+"加上文本消息,最后以"\r\n"结尾 。
比如服务器端返回OK,那么对应的消息就是:
"+OK\r\n"上面的消息是一个非二进制安全的消息,如果想要发送二进制安全的消息,则可以使用Bulk Strings 。
什么是非二进制安全的消息呢?对于Simple Strings来说,因为消息是以"\r\n"结尾,所以消息中间不能包含"\r\n"这两个特殊字符,否则就会产生错误的含义 。
Bulk StringsBulk Strings是二进制安全的 。这是因为Bulk Strings包含了一个字符长度字段,因为是根据长度来判断字符长度的,所以并不存在根据字符中某个特定字符来判断是否字符结束的缺点 。
具体而言Bulk Strings的结构是"$"+字符串长度+"\r\n"+字符串+"\r\n" 。
以OK为例,如果以Bulk Strings来表示,则如下所示:
"$2\r\nok\r\n"Bulk Strings还可以包含空字符串:
"$0\r\n\r\n"当然还可以表示不存在的Null值:
"$-1\r\n"RESP Integers这是redis中的整数表示 , 具体的格式是":"+整数+"\r\n" 。
比如18这个整数就可以用下面的格式来表示:
":18\r\n"RESP Arraysredis的多个命令可以以array来表示,服务器端返回的多个值也可以用arrays来表示 。
RESP Arrays的格式是"*"+数组中的元素个数+其他类似的数据 。
所以RESP Arrays是一个复合结构的数据 。比如一个数组中包含了两个Bulk Strings:"redis","server"则可以用下面的格式来表示:
"*2\r\n$5\r\nredis\r\n$6\r\nserver\r\n"RESP Arrays中的原始不仅可以使用不同类型,还能包含RESP Arrays,也就是array的嵌套:
"*3\r\n$5\r\nredis\r\n$6\r\nserver\r\n*1\r\n$4\r\ngood\r\n"为了方便观察,我们将上面的消息格式一下:
"*3\r\n$5\r\nredis\r\n$6\r\nserver\r\n*1\r\n$4\r\ngood\r\n"上面的消息是一个包含三个元素的数组,前面两个元素是Bulk Strings , 最后一个是包含一个元素的数组 。
RESP Errors最后,RESP还可以表示错误消息 。RESP Errors的消息格式是"-"+字符串,如下所示:
"-Err something wrong\r\n"一般情况下,"-"后面的第一个单词表示的是错误类型,但是这只是一个约定俗成的规定,并不是RESP协议中的强制要求 。
另外,经过对比,大家可能会发现RESP Errors和Simple Strings是消息格式是差不多的 。
这种对不同消息类型的处理是在客户端进行区分的 。
Inline commands如果完全按RESP协议的要求,当我们连接到服务器端的时候需要包含RESP中定义消息的所有格式 , 但是这些消息中包含了额外的消息类型和回车换行符,所以直接使用协议来执行的话会比较困惑 。
于是redis还提供一些内联的命令,也就是协议命令的精简版本,这个精简版本去除了消息类型和回车换行符 。
我们以"get world"这个命令为例 。来看下不同方式的连接情况 。
首先是使用redis-cli进行连接:
redis-cli -h 127.0.0.1127.0.0.1:6379> get world"hello"因为redis-cli是redis的客户端,所以可以直接使用inline command来执行命令 。
如果使用telnet,我们也可以使用同样的命令来获得结果:
telnet 127.0.0.1 6379Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.get world$5hello可以看到返回的结果是"$5\r\nhello\r\n" 。
如果要使用协议消息来请求redis服务器应该怎么做呢?
我们要请求的命令是"get world",将其转换成为RESP的消息则是:
"*2\r\n$3\r\nget\r\n$5\r\nworld\r\n"我们尝试一下将上述命令使用nc传递到redis server上:
(printf "*2\r\n$3\r\nget\r\n$5\r\nworld\r\n"; sleep 1) |nc localhost 6379-ERR Protocol error: expected '$', got ' '很遗憾我们得到了ERR,那么是不是不能直接使用RESP消息格式进行传输呢?当然不是 , 上面的问题在于$符号是一个特殊字符,我们需要转义一下:
(printf "*2\r\n\$3\r\nget\r\n\$5\r\nworld\r\n"; sleep 1) |nc localhost 6379$5hello可以看到输出的结果和直接使用redis-cli一致 。
总结以上就是RESP协议的基本内容和手动使用的例子,有了RESP,我们就可以根据协议中定义的格式来创建redis客户端 。

推荐阅读