前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈 Redis 通信协议

浅谈 Redis 通信协议

原创
作者头像
CodingCode
修改2021-06-03 14:28:51
1.4K0
修改2021-06-03 14:28:51
举报

本文档翻译自:https://redis.io/topics/protocol

Redis 客户端使用称为 RESP (redis 序列化协议)的协议与 Redis 服务器进行通信,虽然是专为Redis设计,但也可用于其他客户端-服务器软件项目。

RESP设计是以下各项之间的折衷:

  • 实现简单
  • 快速解析
  • 可读性强

RESP 可以序列化不同的数据类型,如整数、字符串、数组,还有一种特定的错误类型。请求以字符串数组的形式从客户端发送到Redis服务器,这些字符串表示要执行的命令参数。Redis使用特定命令的数据类型进行回复。

RESP 是二进制安全的,不需要处理从一个进程传输到另一个进程的批量数据,因为它使用前缀长度来传输批量数据。

此处描述的协议仅用于客户端-服务器通信。Redis Cluster使用不同的二进制协议来在节点之间交换信息。

网络层

客户端连接到Redis服务器,创建到端口 6379 的TCP连接。

虽然RESP在技术上是非TCP特定的,但在Redis的上下文中,该协议仅用于TCP连接(或等效的面向流的连接,如 Unix 套接字)。

请求-响应模型

Redis 接受由不同参数组成的命令。收到命令后,将对其进行处理并将回复发送回客户端。

这是最简单的模型,但有两个例外:

  • Redis 支持流水线(本文档稍后介绍)。所以客户端可以一次发送多个命令,然后等待回复。
  • 当Redis客户端订阅Pub/Sub通道时,协议语义发生变化,变成推送协议,即客户端不再需要发送命令,因为服务器会自动向客户端发送新消息(对于通道客户端被订阅)。

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

RESP协议说明

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

RESP 实际上是一种序列化协议,它支持以下数据类型:简单字符串、错误、整数、批量字符串和数组。

RESP 在 Redis 中用作请求-响应协议的方式如下:

  • 客户端将命令作为批量字符串的 RESP 数组发送到 Redis 服务器。
  • 服务器根据命令实现以其中一种 RESP 类型进行回复。

在 RESP 中,某些数据的类型取决于第一个字节:

  • 对于简单字符串,回复的第一个字节是“+”
  • 对于错误,回复的第一个字节是“-”
  • 对于整数,回复的第一个字节是“:”
  • 对于批量字符串,回复的第一个字节是“$”
  • 对于数组,回复的第一个字节是“ *”

此外,RESP 能够使用稍后指定的批量字符串或数组的特殊变体来表示 Null 值。

在 RESP 中,协议的不同部分总是以“\r\n”(CRLF)终止。

RESP 简单字符串

简单字符串按以下方式编码:加号字符,后面不能包含 CR 或 LF 字符(不允许换行)的字符串,以 CRLF 结尾(即“\r\n”)。

简单字符串用于以最小开销传输非二进制安全字符串。例如,许多 Redis 命令在成功时回复“OK”,作为 RESP 简单字符串使用以下 5 个字节进行编码:

代码语言:javascript
复制
"+OK\r\n"

为了发送二进制安全字符串,使用 RESP 批量字符串代替。

当 Redis 回复一个简单字符串时,客户端库应该向调用者返回一个字符串,该字符串由“+”之后的第一个字符组成,直到字符串的末尾,不包括最后的 CRLF 字节。

RESP 错误

RESP 具有特定的错误数据类型。实际上错误与 RESP 简单字符串完全一样,但第一个字符是减号 '-' 字符而不是加号。RESP 中 Simple Strings 和 Errors 的真正区别在于客户端将错误视为异常,而构成 Error 类型的字符串就是错误消息本身。

基本格式为:

代码语言:javascript
复制
"-Error message\r\n"

错误回复仅在发生错误时发送,例如,如果您尝试对错误的数据类型执行操作,或者命令不存在等。当收到错误回复时,库客户端应引发异常。

以下是错误回复的示例:

代码语言:javascript
复制
-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 表示真,0 表示假。

如果操作实际执行,其他命令如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”的编码如下:

代码语言:javascript
复制
"$6\r\nfoobar\r\n"

当一个空字符串只是:

代码语言:javascript
复制
"$0\r\n\r\n"

还可以使用 RESP 批量字符串,以使用用于表示 Null 值的特殊格式来表示值不存在。在这种特殊格式中,长度为 -1,并且没有数据,因此 Null 表示为:

代码语言:javascript
复制
"$-1\r\n"

这称为Null Bulk String

当服务器使用 Null Bulk String 回复时,客户端库 API 不应返回空字符串,而是返回 nil 对象。例如,Ruby 库应返回“nil”,而 C 库应返回 NULL(或在回复对象中设置特殊标志),等等。

RESP 数组

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

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

  • *字符作为第一个字节,然后是数组中元素的数量作为十进制数,然后是 CRLF。
  • 数组的每个元素都是 RESP 类型。

所以一个空数组如下:

代码语言:javascript
复制
"*0\r\n"

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

代码语言:javascript
复制
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

正如您*<count>CRLF在数组前缀部分之后看到的那样,组成数组的其他数据类型只是一个接一个地连接在一起。例如,三个整数的数组编码如下:

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

数组可以包含混合类型,元素不必是相同类型。例如,一个包含四个整数的列表和一个批量字符串可以编码如下:

代码语言:javascript
复制
*5\r\n:1\r\n:2\r\n:3\r\n:4\r\n$6\r\nfoobar\r\n

(为了清楚起见,回复被分成多行)。

服务器发送的第一行是*5\r\n为了指定后面将有五个回复。然后发送构成多批量回复项目的每个回复。

Null Array 的概念也存在,它是指定 Null 值的另一种方式(通常使用 Null Bulk String,但由于历史原因,我们有两种格式)。

例如,当BLPOP命令超时时,它会返回一个空数组,其计数-1如下例所示:

代码语言:javascript
复制
"*-1\r\n"

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

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

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

(为了便于阅读,格式被分成了多行)。

上面的 RESP 数据类型编码一个两个元素的数组,该数组由一个包含三个整数 1、2、3 的数组和一个简单字符串和一个错误的数组组成。

数组中的空元素

Array 的单个元素可能为 Null。这用于 Redis 回复中,以表示这些元素丢失而不是空字符串。当缺少指定的键时,当与 GET模式选项一起使用时,SORT 命令可能会发生这种情况。包含 Null 元素的 Array 回复示例:

代码语言:javascript
复制
*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n

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

代码语言:javascript
复制
["foo",nil,"bar"]

请注意,这不是前几节所说的例外,而只是进一步指定协议的示例。

内联命令

有时在你的手中只有telnet工具,并且你需要发送一个命令到Redis服务器。虽然Redis协议易于实现,但在交互式会话中使用并不理想,而redis-cli可能并不总是可用。出于这个原因,Redis也以一种专门为人类设计的方式接受命令,并被称为内联命令格式。

以下是使用内联命令的服务器/客户端聊天示例(服务器聊天以 S: 开头,客户端聊天以 C: 开头)

代码语言:javascript
复制
C: PINGS: +PONG

以下是返回整数的内联命令的另一个示例:

代码语言:javascript
复制
C: EXISTS somekeyS: :0

基本上,您只需在 telnet 会话中编写以空格分隔的参数。由于*统一请求协议中没有使用以那个开头的命令,Redis 能够检测到这种情况并解析您的命令。

给出一张完备的协议描述图:

图片
图片

图片来自网络

参考:https://redis.io/topics/protocol

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档