Redis协议规范(译文)

原文地址: haifeiWu的博客
博客地址:www.hchstudio.cn
欢迎转载,转载请注明做者及出处,谢谢!redis

Redis客户端使用名为RESP(Redis序列化协议)的协议与Redis服务器进行通讯。 虽然该协议是专为Redis设计的,但它能够用于其余CS软件项目的通信协议。数组

RESP是如下几方面的考虑:安全

  • 易于实现
  • 快速解析
  • 可读性高

RESP能够序列化不一样的数据类型,如整型,字符串,数组。 还有一种特定的错误类型。 请求将要执行的命令做为字符串数组从Redis客户端发送到Redis服务器。Redis使用特定数据类型的命令进行回复。bash

RESP是二进制安全的,不须要处理从一个进程传输到另外一个进程的批量数据,由于它使用前缀长度来传输批量数据。服务器

注意: 此处概述的协议仅用于客户端 - 服务器通讯。 Redis Cluster使用不一样的二进制协议,以便在节点之间交换消息。网络

网络层

客户端链接到Redis服务器,是建立TCP链接到端口6379。 虽然RESP在技术上是非TCP特定的,但在Redis的上下文中,协议仅用于TCP链接(或相似的面向流的链接,如Unix套接字)。
ui

请求 - 响应模型

Redis接受由不一样参数组成的命令。 收到命令后,将对其进行处理并将回复发送回客户端。 这是最简单的模型,但有两个例外:编码

  • Redis支持流水线操做(本文档稍后介绍)。 所以,客户端能够一次发送多个命令,并等待稍后的回复。
  • 当Redis客户端处于 Pub/Sub 时,协议会更改语义并成为推送协议,即客户端再也不须要发送命令,由于服务器会在它们接收到命令时发自动向客户端发送新消息。

排除上述两个例外,Redis协议是一个简单的请求 - 响应协议。
spa

RESP 协议描述

RESP协议在Redis 1.2中引入,但它成为与Redis 2.0中的Redis服务器通讯的标准方式。 这是每个Redis客户端中应该实现的协议。翻译

RESP其实是一个支持如下数据类型的序列化协议:单行字符串,错误信息,整型,多行字符串和数组。 RESP在Redis中用做请求 - 响应协议的方式以下:

  • 客户端将命令做为字符串数组发送到Redis服务器。
  • 服务器根据命令实现回复一种RESP类型数据。

在 RESP 中, 一些数据的类型经过它的第一个字节进行判断:

  • 单行回复:回复的第一个字节是 "+"

  • 错误信息:回复的第一个字节是 "-"

  • 整形数字:回复的第一个字节是 ":"

  • 多行字符串:回复的第一个字节是 "$"

  • 数组:回复的第一个字节是 "*"

此外,RESP可以使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。 在RESP中,协议的不一样部分始终以“\ r \ n”(CRLF)结束。

RESP 单行字符串(简单字符串)

简单字符串按如下方式编码:加号字符,后跟不能包含CR或LF字符的字符串(不容许换行),由CRLF终止(即“\ r \ n”)。
Simple Strings用于以最小的开销传输非二进制安全字符串。 例如,许多Redis命令成功回复时只有“OK”,由于RESP 单行字符串使用如下5个字节进行编码:

"+OK\r\n"
复制代码

为了发送二进制安全字符串,使用RESP 多行字符串代替。
当Redis使用Simple String回复时,客户端库应该向调用者返回一个字符串,该字符串由“+”以后的第一个字符组成,直到字符串结尾,不包括最终的CRLF字节。

RESP 错误信息

RESP具备错误的特定数据类型。 实际上错误与RESP 单行字符串彻底相同,但第一个字符是减号' - '字符而不是加号。

RESP中单行字符串和错误之间的真正区别在于客户端将错误视为异常,组成错误类型的字符串是错误消息自己。 基本格式以下:

"-Error message\r\n"
复制代码

错误回复仅在发生错误时发送,例如,若是您尝试对错误的数据类型执行操做,或者命令不存在等等。 收到错误回复时,客户端应将异常抛出。

如下是错误回复的示例:

-ERR unknown command 'foobar'
-WRONGTYPE Operation against a key holding the wrong kind of value
复制代码

“ - ”以后的第一个单词,直到第一个空格或换行符,表示返回的错误类型。 这只是Redis使用的约定,不是RESP错误格式的一部分。

例如,ERR是通常错误,而WRONGTYPE是一个更具体的错误,意味着客户端尝试对错误的数据类型执行操做。 这称为错误前缀,是一种容许客户端理解服务器返回的错误类型的方法,而不依赖于给定的确切消息,这可能随时间而变化。

客户端实现能够针对不一样的错误返回不一样类型的异常,或者能够经过直接将错误名称做为字符串提供给调用者来提供捕获错误的通用方法。

可是,这样的功能不该该被认为是相当重要的,由于它不多有用,而且有限的客户端实现可能只返回通用的错误条件,例如false。

RESP 整型数据

此类型只是一个CRLF终止的字符串,表示一个以“:”字节为前缀的整数。 例如“:0 \ r \ n”或“:1000 \ r \ n”是整数回复。 许多Redis命令返回RESP 整型,如INCR,LLEN和LASTSAVE。

返回的整数没有特殊含义,它只是INCR的增量编号,LASTSAVE的UNIX时间等等。 可是,返回的整数应保证在有符号的64位整数范围内。

整数回复也被普遍使用,以便返回真或假。 例如,EXISTS或SISMEMBER之类的命令将返回1表示true,0表示false。

若是实际执行操做,其余命令(如SADD,SREM和SETNX)将返回1,不然返回0。

如下命令将回复整数回复:SETNX,DEL,EXISTS,INCR,INCRBY,DECR,DECRBY,DBSIZE,LASTSAVE,RENAMENX,MOVE,LLEN,SADD,SREM,SISMEMBER,SCARD。

RESP 多行字符串

多行字符串用于表示长度最大为512 MB的单个二进制安全字符串。

多行字符串按如下方式编码:

  • 一个“$”字节后跟组成字符串的字节数(一个前缀长度),由CRLF终止。
  • 字符串数据。
  • 最终的CRLF。

因此字符串“foobar”的编码以下:

"$6\r\nfoobar\r\n"
复制代码

当只是一个空字符串时:

"$0\r\n\r\n"
复制代码

RESP 多行字符串也可用于使用用于表示Null值的特殊格式来表示值的不存在。 在这种特殊格式中,长度为-1,而且没有数据,所以Null表示为:

"$-1\r\n"
复制代码

当服务器使用Null 多行字符串回复时,客户端库API不该返回空字符串,而应返回nil对象。 例如,Ruby库应返回'nil',而C库应返回NULL(或在reply对象中设置特殊标志),依此类推。

RESP 数组

客户端使用RESP 数组将命令发送到Redis服务器。 相似地,某些Redis命令将元素集合返回给客户端使用RESP 数组是回复类型。 一个例子是LRANGE命令,它返回列表的元素。

RESP数组使用如下格式发送:

  • *字符做为第一个字节,后跟数组中的元素数做为十进制数,后跟CRLF。
  • 数组的每一个元素的附加RESP类型。

因此空数组就是如下内容:

"*0\r\n"
复制代码

那么两个RESP批量字符串“foo”和“bar”的数组编码为:

"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
复制代码

正如您在数组前面加上* CRLF部分以后所看到的那样,组成数组的其余数据类型将一个接一个地链接起来。 例如,三个整数的数组编码以下:

"*3\r\n:1\r\n:2\r\n:3\r\n"
复制代码

数组能够包含混合类型,元素没必要具备相同的类型。 例如,四个整数和批量字符串的列表能够编码以下:

*5\r\n
:1\r\n
:2\r\n
:3\r\n
:4\r\n
$6\r\n
foobar\r\n
复制代码

服务器发送的第一行是* 5 \ r \ n,以指定将跟随五个回复。 而后发送构成多重回复项目的每一个回复。

Null 数组的概念也存在,而且是指定Null值的替代方法(一般使用Null 多行字符串,但因为历史缘由,咱们有两种格式)。

例如,当BLPOP命令超时时,它返回一个计数为-1的Null数组,以下例所示:

"*-1\r\n"
复制代码

当Redis使用Null数组回复时,客户端库API应返回空对象而不是空数组。 这是区分空列表和不一样条件(例如BLPOP命令的超时条件)所必需的。

RESP中可使用数组中嵌套数组。 例如,两个数组的数组编码以下:

*2\r\n
*3\r\n
:1\r\n
:2\r\n
:3\r\n
*2\r\n
+Foo\r\n
-Bar\r\n
复制代码

第二个元素是Null。 客户端库应返回以下内容:

["foo",nil,"bar"]
复制代码

注意,这不是前面部分中所述的例外,而只是进一步指定协议的示例。

发送命令到 Redis 服务端

既然熟悉RESP序列化格式,那么编写Redis客户端库的实现将很容易。 咱们能够进一步讲述客户端和服务器之间的交互如何工做:

  • 客户端向Redis服务器发送仅由Bulk Strings组成的RESP阵列。
  • Redis服务器回复发送任何有效RESP数据类型做为回复的客户端。

所以,例如,典型的交互能够是如下所示。

客户端发送命令LLEN mylist以获取存储在密钥mylist中的列表长度,服务器回复一个Integer回复,以下例所示(C:是客户端,S:服务器)。

C: *2\r\n
C: $4\r\n
C: LLEN\r\n
C: $6\r\n
C: mylist\r\n
S: :48293\r\n
复制代码

一般咱们将协议的不一样部分与换行符分开以简化,但实际的交互是客户端发送 * 2 \ r \ n 4 \ r \ nLLEN \ r \ n 6 \ r \ nmylist \ r \ n 总体。

小结

这是楼主第一次尝试翻译一篇技术文档,相对来讲技术文档的英文阅读起来仍是比较舒服的,相信有了第一次尝试,以后确定会愈来愈顺利。因为楼主水平有限,文章中不免有纰漏,指望小伙伴的指出,感谢……。

参考连接

相关文章
相关标签/搜索