RESP

第七章 Redis序列化协议(REdis Serialization Protocol)

1 概述

Redis系统分为Redis客户端和Redis服务器端。Redis客户端通过使用Redis序列化协议向Redis服务器发送命令和接收数据。Redis序列化协议称为REdis Serialization Protocol,简称RESP。 RESP可用于任何系统的通信,也可建立在任何常用的网络协议之上。在Redis系统中,RESP是建立于TCP协议上的,Redis服务器默认监听6379端口。

RESP是一个基于文本的协议(Text-based Protocol)。 Redis设计人员并没有采用二进制的格式,这是因为:其一、Redis系统的效率非常高,而且瓶颈不在于CPU上。所以,Redis服务器可以花费一些额外的CPU资源解析RESP中的文本;其二,RESP是一个简单的协议,易于实现、其三,采用文本协议易于观察和调试,毕竟,所有的内容都是文本,开发人员可以直接阅读。

本章将重点放在RESP协议的内容上,讲解客户端如何与服务器通信。理解了RESP协议之后,我们就能理解Redis客户端的工作方式了。

2 RESP协议内容

RESP协议遵守“一问一答”的方式,即,当客户端向服务器发送一个请求后,客户端能立刻收到服务器的应答。在服务器端,当服务器收到一个请求后,服务器立即处理该请求,并发出应答。这种"一问一答"的工作方式非常简单,易于实现。在这种工作方式下,一个例外是客户端可以使用Pipeline,即客户端一次可以发多条请求给服务器,然后将服务器的应答缓存起来,一次性处理应答。在Pipeline的工作方式下,服务器的工作方式不变:每收到一个请求,则处理一个请求,并返回处理结果。 另一个例外就是发布-订阅系统。当客户端订阅了一个频道之后,Redis服务器会向客户端推送消息。此时,RESP协议从"一问一答"方式变为单向的消息传递方式。

2.1 RESP协议的基本数据类型

RESP协议支持五种数据类型,它们是简单字符串(Simple Strings)、错误(Errors)、整数(Integers)、长字符串(Bulk Strings)和数组(Arrays)。 客户端向服务器端发送的命令请求是由Bulk Strings数组(An Array of Bulk Strings)构成的。 服务器的应答则由以上五种数据类型组成。

  1. 简单字符串(Simple Strings)。简单字符串是由一个"+"符号,加上字符串的内容,再加上回车换行符("\r\n")构成。所以,简单字符串的第一个字符永远是一个加号。因为最后的结束字符是回车换行符,所以,中间的字符串内容中不能包含回车或者换行符。下面是一个简单字符串的例子。
"+OK\r\n"
  1. 错误类型(Errors)。从实现来看,错误类型与简单字符串类型非常相似。为了能将服务器返回的错误描述与正常输出区分开,RESP为出错信息专门设计了一个数据类型。错误类型的数据由一个减号"-"开始,然后紧接着错误信息,最后由回车换行符("\r\n")结束。下面是一个错误类型的例子。
"-ERR unkown command 'foobar'.\r\n"

在此基础之上,Redis服务器进一步制定了解析错误类型数据的规则,但是,这些规则并不是RESP协议中的一部分。在减号之后"-",第一个出现的单词表示着出错的类型。所以,客户端能根据第一个单词来决定发生的异常类型。这仅仅是Redis服务器提供的规则,Redis客户端可以使用这个规则,也可以自由决定处理方法。

  1. 整数类型(Integers)。整数类型以冒号开始,中间包含一个字符串格式的整数,最后由回车换行符("\r\n")结束。下面是一个整数10的例子。
":10\r\n"
  1. 长字符串(Bulk Strings)类型。除了简单字符串,Redis还支持长字符串(Bulk Strings)类型,这是因为:其一、长字符串类型可支持长达512 MB的字符串。因此,Redis客户端可使用长字符串(Bulk Strings)向服务器发送大块数据。然而,Redis服务器返回的响应消息的长度一般很短,使用简单字符串类型足以表达。其二、长字符串是Binary Safe的,它遵守Redis服务器端Key的格式要求。在Redis系统中,Binary Safe是指可以包含任何取值的数据类型。简单字符串不是Binary Safe,因为简单字符串中不能包含回车或者换行符。长字符串因为有长度字段,所以,Redis只需按照长度读取数据即可。

长字符串类型的数据由一个美元符号"$"开始,然后紧接着一个整形类型的数据表示字符串的长度,然后,再接着是字符串的内容。最后,以回车换行符("\r\n")结束。例如:如下是表示"Little Waterdrop"字符串的长字符串类型的例子。值得注意的是,字符串的长度与字符串的内容之间由回车换行符("\r\n")分割。

"$16\r\nLittle Waterdrop\r\n"

因为有字符串长度字段的帮助,长字符串还能表达空字符串和Null类型的数据,例如,下面第一个例子是一个空字符串,其长度为0。第二个例子用于表示Null,其长度字段为-1,没有字符串内容(这是RESP协议的规定)。

"$0\r\n\r\n"
"$-1\r\n"
  1. 数组(Arrays)类型。数组类型将上述的数据聚合在一起,组成了一个数组。数组中的元素可以是任意上述的数据类型。一个数组可以包含不同类型的元素。数组类型的数据由一个星号"*"开始,紧接着是数组元素的个数,最后是各个元素的数据。例如,下面第一个例子是一个空数组,它的数组元素个数为0,该数组不包含任何元素。第二个例子是一个包含了两个长字符串"foor"和"bar"的数组。值得注意的是,在数组元素个数和元素数据之间由回车换行符("\r\n")分隔。
"*0\r\n"
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

2.2 RESP协议的例子

我们在一台服务器上启动了一个Redis服务器和一个Redis客户端,并从客户端上输入"set key value"命令。所以,客户端会按照RESP协议的要求向Redis服务器发送命令。下面是使用tcpdump工具抓取的数据包,从倒数第三行起(位移在0x0034起)是发送的命令的内容。该内容是"*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"。这是一个有着3个元素的数组。第一个元素长度为3,第二个元素的长度为3,第三个元素的长度为5,如下图所示。

图一 Redis命令例子

图一 Redis命令例子。

下面展示的是tcpdump命令抓取的数据包的全部内容。

> tcpdump -vv -x -i lo port 6379
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
21:09:07.723328 IP (tos 0x0, ttl 64, id 36034, offset 0, flags [DF], proto TCP (6), length 85)
    localhost.42312 > localhost.6379: Flags [P.], cksum 0xfe49 (incorrect -> 0x3d06), seq 751800028:751800061, ack 3639686382, win 1024, options [nop,nop,TS val 3559180120 ecr 3559174905], length 33: RESP "set" "key" "value"
        0x0000:  4500 0055 8cc2 4000 4006 afde 7f00 0001
        0x0010:  7f00 0001 a548 18eb 2ccf 8edc d8f1 34ee
        0x0020:  8018 0400 fe49 0000 0101 080a d424 c758
        0x0030:  d424 b2f9 2a33 0d0a 2433 0d0a 7365 740d
        0x0040:  0a24 330d 0a6b 6579 0d0a 2435 0d0a 7661
        0x0050:  6c75 650d 0a

在抓取的数据包中,上述命令的字符串内容在最后三行。这些数据是十六进制的数据,我们将命令内容截取出来,展示在下图中。字符串的内容可以参照ASCII表转换得到。0x2a表示的是字符星号;0x33表示的数字3;以此类推。

        0x0030:            2a33 0d0a 2433 0d0a 7365 740d
        0x0040:  0a24 330d 0a6b 6579 0d0a 2435 0d0a 7661
        0x0050:  6c75 650d 0a

3 小结

我们在本章中介绍了Redis客户端和服务器端通信的协议RESP。它是一个基于文本的协议,可以建立在任何网络协议之上。Redis采用文本协议的原因是RESP易于实现、易于观察。虽然二进制协议(Binary Protocol)在性能上有一定优势,但是,Redis开发人员认为Redis的性能瓶颈并不在协议处理上。所以,使用文本协议并不会降低Redis的性能。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.