一文彻底理解Redis序列化协议,你也可以编写Redis客户端

本文详细介绍了Redis的RESP(REdis Serialization Protocol)协议,包括其设计目标、网络层、请求-响应模型以及支持的数据类型。重点解析了简单字符串、错误消息、整型数字、定长字符串和数组的编码与解码过程,帮助读者掌握如何基于RESP编写高性能的Redis客户端解析器。
摘要由CSDN通过智能技术生成

前提#

最近学习Netty的时候想做一个基于Redis服务协议的编码解码模块,过程中顺便阅读了Redis服务序列化协议RESP,结合自己的理解对文档进行了翻译并且简单实现了RESP基于Java语言的解析。编写本文的使用使用的JDK版本为[8+]

RESP简介#

Redis客户端与Redis服务端基于一个称作RESP的协议进行通信,RESP全称为Redis Serialization Protocol,也就是Redis序列化协议。虽然RESPRedis设计,但是它也可以应用在其他客户端-服务端(Client-Server)的软件项目中。RESP在设计的时候折中考虑了如下几点:

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

RESP可以序列化不同的数据类型,如整型、字符串、数组还有一种特殊的Error类型。需要执行的Redis命令会封装为类似于字符串数组的请求然后通过Redis客户端发送到Redis服务端。Redis服务端会基于特定的命令类型选择对应的一种数据类型进行回复(这一句是意译,原文是:Redis replies with a command-specific data type)。

RESP是二进制安全的(binary-safe),并且在RESP下不需要处理从一个进程传输到另一个进程的批量数据,因为它使用了前缀长度(prefixed-length,后面会分析,就是在每个数据块的前缀已经定义好数据块的个数,类似于Netty里面的定长编码解码)来传输批量数据。

注意:此处概述的协议仅仅使用在客户端-服务端通信,Redis Cluster使用不同的二进制协议在多个节点之间交换消息(也就是Redis集群中的节点之间并不使用RESP通信)。

网络层#

Redis客户端通过创建一个在6379端口的TCP连接,连接到Redis服务端。

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

请求-响应模型#

Redis服务端接收由不同参数组成的命令,接收到命令并将其处理之后会把回复发送回Redis客户端。这是最简单的模型,但是有两种例外的情况:

  • Redis支持管道(Pipelining,流水线,多数情况下习惯称为管道)操作。使用管道的情况下,Redis客户端可以一次发送多个命令,然后等待一次性的回复(文中的回复是replies,理解为Redis服务端会一次性返回一个批量回复结果)。
  • Redis客户端订阅Pub/Sub信道时,该协议会更改语义并成为推送协议(push protocol),也就是说,客户端不再需要发送命令,因为Redis服务端将自动向客户端(订阅了改信道的客户端)发送新消息(这里的意思是:在订阅/发布模式下,消息是由Redis服务端主动推送给订阅了特定信道的Redis客户端)。

除了上述两个特例之外,Redis协议是一种简单的请求-响应协议。

RESP支持的数据类型#

RESPRedis 1.2中引入,在Redis 2.0RESP正式成为与Redis服务端通信的标准方案。也就是如果需要编写Redis客户端,你就必须在客户端中实现此协议。

RESP本质上是一种序列化协议,它支持的数据类型如下:单行字符串、错误消息、整型数字、定长字符串和RESP数组。

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

  • Redis客户端将命令封装为RESP的数组类型(数组元素都是定长字符串类型,注意这一点,很重要)发送到Redis服务器。
  • Redis服务端根据命令实现选择对应的RESP数据类型之一进行回复。

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

  • 单行字符串的第一个字节为+
  • 错误消息的第一个字节为-
  • 整型数字的第一个字节为:
  • 定长字符串的第一个字节为$
  • RESP数组的第一个字节为*

另外,在RESP中可以使用定长字符串或者数组的特殊变体来表示Null值,后面会提及。在RESP中,协议的不同部分始终以\r\nCRLF)终止

目前RESP中5种数据类型的小结如下:

下面的小节是对每种数据类型的更细致的分析。

RESP简单字符串-Simple String#

简单字符串的编码方式如下:

  • (1)第一个字节为+
  • (2)紧接着的是一个不能包含CR或者LF字符的字符串。
  • (3)以CRLF终止。

简单字符串能够保证在最小开销的前提下传输非二进制安全的字符串。例如很多Redis命令执行成功后服务端需要回复OK字符串,此时通过简单字符串编码为5字节的数据报如下:

+OK\r\n

 

如果需要发送二进制安全的字符串,那么需要使用定长字符串。

Redis服务端用简单字符串响应时,Redis客户端库应该向调用者返回一个字符串,该响应到调用者的字符串由+之后直到字符串内容末尾的字符组成(其实就是上面提到的第(2)部分的内容),不包括最后的CRLF字节。

RESP错误消息-Error#

错误消息类型是RESP特定的数据类型。实际上,错误消息类型和简单字符串类型基本一致,只是其第一个字节为-。错误消息类型跟简单字符串类型的最大区别是:错误消息作为Redis服务端响应的时候,对于客户端而言应该感知为异常,而错误消息中的字符串内容应该感知为Redis服务端返回的错误信息。错误消息的编码方式如下:

  • (1)第一个字节为-
  • (2)紧接着的是一个不能包含CR或者LF字符的字符串。
  • (3)以CRLF终止。

一个简单的例子如下:

-Error message\r\n

Redis服务端只有在真正发生错误或者感知错误的时候才会回复错误消息,例如尝试对错误的数据类型执行操作或者命令不存在等等。Redis客户端接收到错误消息的时候,应该触发异常(一般情况就是直接抛出异常,可以根据错误消息的内容进行异常分类)。下面是错误消息响应的一些例子:

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

-之后的第一个单词到第一个空格或换行符之间的内容,代表返回的错误类型。这只是Redis使用的约定,不是RESP错误消息格式的一部分。

例如,ERR是通用错误,WRONGTYPE则是更具体的错误,表示客户端试图针对错误的数据类型执行操作。这种定义方式称为错误前缀,是一种使客户端能够理解服务器返回的错误类型的方法,而不必依赖于所给出的确切消息定义,该消息可能会随时间而变化。

客户端实现可以针对不同的错误类型返回不同种类的异常,或者可以通过将错误类型的名称作为字符串直接提供给调用方来提供捕获错误的通用方法。

但是,不应该将错误消息分类处理的功能视为至关重要的功能,因为它作用并不巨大,并且有些的客户端实现可能会简单地返回特定值去屏蔽错误消息作为通用的异常处理,例如直接返回false

RESP整型数字-Integer#

整型数字的编码方式如下:

  • (1)第一个字节为
  • (2)紧接着的是一个不能包含
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾听铃的声

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值