专栏首页用户7890857的专栏浅谈 Redis 通信协议
原创

浅谈 Redis 通信协议

本文档翻译自: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 个字节进行编码:

"+OK\r\n"

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

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

RESP 错误

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

基本格式为:

"-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 表示真,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”的编码如下:

"$6\r\nfoobar\r\n"

当一个空字符串只是:

"$0\r\n\r\n"

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

"$-1\r\n"

这称为Null Bulk String

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

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"

正如您*<count>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\nfoobar\r\n

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

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

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

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

"*-1\r\n"

当 Redis 用 Null Array 回复时,客户端库 API 应该返回一个 null 对象而不是一个空的 Array。这是区分空列表和不同条件(例如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

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

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

数组中的空元素

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

*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n

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

["foo",nil,"bar"]

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

内联命令

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

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

C: PINGS: +PONG

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

C: EXISTS somekeyS: :0

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

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

图片来自网络

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

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

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

关注作者,阅读全部精彩内容

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 浅谈Redis通信协议

    Redis客户端和服务器端使用的通信协议叫做RESP(Redis Serialization Protocol)。它是特意为Redis设计的,同时也可以用于其他...

    Jackeyzhe
  • Redis 通信协议

    几乎所有的主流编程语言都有Redis的客户端(http://redis.io/clients),不考虑Redis非常流行的原因,如果站在技术的角度看原因还有两个...

    三产
  • redis 通信协议,php实现redis协议

    string(34) "*3CRLF$3CRLFsetCRLF$1CRLFa$8CRLFtioncicoCRLF"

    仙士可
  • Redis网络通信协议说明

    Redis在TCP端口6379上监听到来的连接,客户端连接到来时,Redis服务器为此创建一个TCP连接。在客户端与服务器端之间传输的每个Redis命令或者数据...

    JavaEdge
  • HTTP协议基础浅谈

    HTTP协议是超文本传输协议的缩写,英文是Hyper Text Transfer Protocol。是从万维网服务器传输超文本到本地浏览器的传送协议。

    吾非同
  • 通信协议

      由于没有存储共享器,分布式系统中的所有通信都是基于底层消息交换的。如果进程A要与进程B通信,A必须首先在自己的地址空间中生成该消息,再执行一个系统调用,通知...

    用户3003813
  • 浅谈IPv4协议与IPv6协议的区别!

    在讲IPv4协议与IPv6协议的区别前,我们必须了解什么叫IPv4协议和IPv6协议?

    墨者盾
  • 浅谈TCP IP协议栈(四)IP协议解析

    通过之前的网络层基础知识,IP地址以及路由器的简介,大家应该对于TCP/IP有一个大致的了解,在脑海里应该对于网络的几个基础概念有个大概的了解,简单点说整个协议...

    233333
  • 【原创】浅谈网络协议

    网络绝杀秘籍:OSI和TCP/IP是很基础但又非常重要的网络基础知识,理解得透彻对我们来说非常有帮助。一起来和小编复习下吧..... OSI七层模型: ...

    码神联盟
  • ERC20 协议 Token 钱包浅谈

    这是「区块链技术指北」的第 3 篇文章。 以太坊,Ethereum 是一个分布式的计算机,有许多的节点,其中的每一个节点,都会执行字节码(其实就是智能合约),然...

    robinwen
  • 浅谈网络协议:TCP 篇

    从确保双端收发能力正常的角度理解,==三次握手是能让客户端和服务端确信自己和对方的收发能力正常所需的最少次数==:

    Chor
  • 浅谈网络协议:DNS 篇

    DNS 中所说的记录,指的是域名和 IP 的对应关系。根据使用场景,有不同类型的记录:

    Chor
  • 浅谈WebSocket协议、WS协议和WSS协议原理及关系

    现如今,一些游戏,网站,APP,支付行业的网络经常会收到DDOS和CC,因此这个事情也变得不足为奇了。但在防护方案中有多种不同的方案。如,今天有几个同事在针对高...

    墨者安全科技
  • HTTP 通信协议

    用户2141593
  • [android] android通信协议

    4.2简单代码处理:时间戳(SimpleDateFormat)+随机值(Random)

    陶士涵
  • 浅谈物联网开发最热协议—MQTT协议

    最近在在物联网相关项目的开发的时候,有用到物联网常用的通信协议--MQTT协议,刚开始对这一块的知识并不是很了解,所以在这里一边学习一边记录,有不合理的地方还希...

    灰小猿
  • 今日推荐:awesome-architecture

    但是这条路还是有很多人走,而且也留下了相应的封神之法,今天推荐的就是一个相当详细的架构师框架学习图。内容很充实,看目录的时候,滚动条滚了很多次!学习起来肯定也不...

    仇诺伊
  • BAT最新Java面试题汇总:并发编程+JVM+Spring+分布式+缓存等!

    今天给大家分享下我整理的Java架构面试专题及答案,其中大部分都是大企业面试常问的面试题,可以对照这查漏补缺,当然了,这里所列的肯定不可能覆盖全部方式,不过也希...

    javaworld
  • 通信协议详解

    传统意义上的“通讯”主要指电话、电报、电传。通讯的“讯”指消息(Message),媒体讯息通过通讯网络从一端传递到另外一端。媒体讯息的内容主要是话音、文字、图片...

    PM吃瓜

扫码关注云+社区

领取腾讯云代金券