好多年前看过redis的代码,那个时候还是2.6的版本,集群和哨兵还没加入正式代码,这几年redis发展的好快。简略翻译一篇文章redis的https://redis.io/topics/protocol

redis的客户端和服务器通过一种叫RESP (REdis Serialization Protocol)协议进行通讯。虽然他是为redis设计的协议,但是也可以用到其他的CS架构软件里。

RESP的设计主要考虑了以下几个要求:

  • 容易实现
  • 快速解析
  • 人类可读

RESP能够序列化不同的数据类型像整型,字符串,数组。还有一种专门为错误设计的类型。从客户端发给服务器的请求是一个代表命令参数的数组。Redis返回一个该命令指定的数据类型。

RESP是二进制安全的而且从一个进程发块数据给另一个进程的时候不需要做转换,因为他会在块数据之前加上长度。

注意:这个协议只用于redis的客户端和服务器的信息交互。redis集群的node之间使用另一种二进制协议来交换信息。

网络层

redis的客户端和服务器通过6379端口建立连接。

虽然RESP是不依赖TCP协议的,但是这个协议只用在TCP连接上(或者是其他的流协议,比如unix套接字)。

请求-应答模型

redis能够接收带各种类型参数的命令。处理收到的命令并且返回应答。这是最简单的模型,但是有两个例外:

  • redis支持管道,所以客户端可能一次发送多个命令然后等待应答。
  • 如果redis客户端使用了发布/订阅模式(Pub/Sub),这个协议就变成了推送协议(push protocol),也就是说客户端不用再发命令了,服务器收到相应消息的时候会自动发给客户端。

除了以上两种情况,redis协议是一个简单的请求应答协议。

RESP协议描述

RESP协议是在redis的1.2版本引入的,之后在2.0版本成为标准协议,需要在客户端实现。

RESP是一种序列化协议,支持以下类型:简单字符串(sample strings),错误(Errors),整型(integers),块字符串(bulk strings)和数组(arrays)。

在RESP里,数据类型是第一个字节决定的:

  • 第一个字节是“+”代表简单字符串类型。
  • 第一个字节是“-”代表错误类型。
  • 第一个字节是“:”代表整型。
  • 第一个字节是“$”代表块字符串。
  • 第一个自己是“*”代表数组。

RESP能用一个特殊的块字符串代表NULL。RESP协议不同的部分用“\r\n”(CRLF)结尾。

RESP简单字符串

简单字符串用如下方法编码:一个“+”号后面跟字符串,最后是“\r\n”,字符串里不能包含"\r\n"。简单字符串用来传输比较短的二进制安全的字符串。例如很多redis命令执行成功会返回“OK”,用RESP编码就是5个字节:

"+OK\r\n"

想要发送二进制安全的字符串,需要用RESP的块字符串。当redis返回了一个简单字符串的时候,客户端库需要给调用者返回“+”号(不含)之后CRLF之前(不含)的字符串。

RESP错误

RESP有一种专门为错误设计的类型。实际上错误类型很像RESP简单字符串类型,但是第一个字符是“-”。简单字符串类型和错误类型的区别是客户端把错误类型当成一个异常,错误类型包含的字符串是异常信息。格式是:

"-Error message\r\n"

有错误发生的时候才会返回错误类型,例如你执行了一个对于某类型错误的操作,或者命令不存在等。当返回一个错误类型的时候客户端库应该发起一个异常。下面是一个错误类型的例子

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

“-”号之后空格或者换行符之前的字符串代表返回的错误类型,这只是惯例,并不是RESP要求的格式。例如ERR是一般错误,WRONGTYPE是更具体的错误表示客户端的试图在错误的类型上执行某个操作。这个称为错误前缀,能让客户端更方便的识别错误类型。

客户端可能为不同的错误返回不同的异常,也可能只提供一个一般的方法来捕捉错误并提供错误名。但是不能依赖客户端提供的这些特性,因为有的客户端仅仅返回一般错误,比如false。

RESP整型

RESP只是用一个CRLF结尾的字符串代表整型,第一个自己是“:”,例如":0\r\n"或者":1000\r\n"是整型返回。有很多redis命令返回整型,比如INCR,LLEN和LASTSVAE。返回的整型没有特定的意义,对于INCR就是一个增长后的值,对于LASTSAVE就是一个UNIX时间戳。虽然没什么意义,但是还是给他分配了一个64位的有符号的空间。整型返回值还可以用于返回true或者false,比如命令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块字符串

块字符串用来代表二进制安全的字符从,长度可达512M。块字符串用如下方式编码:

  • “$”开头,跟着数字代表字符串长度,最后是CRLF。
  • 字符串。
  • 最后CRLF。

所以“foobar”会编码成如下方式:

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

空字符串如下:

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

RESP块字符串还能用一个特殊的格式来表示一个不存在的值,代表NULL值。在这个特殊的格式里长度是-1,不带数据。所以NULL用如下格式表示:

"$-1\r\n"

这个被称为空块字符串(NULL BULK STRING)。客户端库API不能返回空字符串,当服务端返回空块字符串的时候客户端需要返回nil。例如RUBY库需要返回‘nil’,C库需要返回NULL(或者在返回值里设置一个特殊的标志)。

RESP数组

redis客户端用RESP数组给服务器发命令。一些命令的返回值也是RESP数组类型,比如LRANGE。RESP数组使用如下格式:

  • “*”号作为第一个字符,跟着一个数字代表元素个数,后面一个CRLF。
  • 每个元素是一个RESP类型。

空数组如下:

"*0\r\n"

包含两个块字符串“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\n
foobar\r\n

服务器第一行发送*5\r\n代表后面跟着5个元素,之后传送每一个元素。空数组是存在的,也可以用来表示空值(一般情况下用空块字符串,但是因为历史原因两种都可以用)。例如BLPOP命令超时,他会返回一个空数组,如下:

"*-1\r\n"

当服务器返回空数组以后客户端的库API应该返回一个空对象而不是空数组,区分不同状态下的空数组是有必要的。在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

上面是一个包含三个整数的数组和一个简单字符串数组组成的数组。

包含空元素的数组

数组里的一个元素可以为空。可以用在应答里来表明这个元素丢失了。如下数组包含一个空元素:

*3\r\n
$3\r\n
foo\r\n
$-1\r\n
$3\r\n
bar\r\n

第二个元素是空,客户端库应该返回如下:

["foo",nil,"bar"]

注意这不是上一节说的异常,而是为了进一步说明RESP协议。

给REDIS服务器发命令

现在你熟悉了RESP序列化,写一个REDIS客户端库应该不难。我们来进一步说明客户端和服务器是怎样交互的:

  • 客户端给服务器端发送只包含块字符串的数组。
  • 服务器会给客户端发送任何合法的RESP数据类型。

一个典型的交互是下面这样的,客户端为了获取mylist链表的长度发送LLEN mylist命令给服务器,服务器返回一个整数作为应答:

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

多命令和管道

如果你需要给redis服务器发命令,但是手头只有telnet工具怎么办呢?尽管redis协议是很容易实现的,但是用这种交互型的工具实现redis协议也不是理想的工具。因此redis也可以用一种特殊的方式接受人类可读的命令,称为内联格式(inline command)。如下是客户端服务器用内联格式的例子:

C: PING
S: +PONG

下面是另一个内联格式,返回一个整数:

C: EXISTS somekey
S: :0

其实就是简单的把参数用空格分开,因为命令都不是“*”开头的,redis就会检测这种形式并且解析。

redis协议的高性能解析器

略吧,这一段没什么意思。

redis协议规范的更多相关文章

  1. Redis协议规范(RESP)

    Redis 即 REmote Dictionary Server (远程字典服务): 而Redis的协议规范是 Redis Serialization Protocol (Redis序列化协议) 该协 ...

  2. Redis协议规范(译文)

    Redis客户端使用名为RESP(Redis序列化协议)的协议与Redis服务器进行通信. 虽然该协议是专为Redis设计的,但它可以用于其他CS软件项目的通讯协议. RESP是以下几方面的考虑: 易 ...

  3. Redis随笔(六)RESP的协议规范

    1.官网文档 https://redis.io/topics/protocol http://www.redis.cn/topics/protocol.html 2.协议介绍 redis协议规范(Re ...

  4. 从零单排学Redis【白银】

    前言 只有光头才能变强 今天继续来学习Redis,上一篇从零单排学Redis[青铜]已经将Redis常用的数据结构过了一遍了.如果还没看的同学可以先去看一遍再回来~ 这篇主要讲的内容有: Redis服 ...

  5. 使用 DotNetty 实现 Redis 的一个控制台应用程序

    零:Demo 跑出来的结果如图 上图说明 图中左边蓝色的命令行界面,是用windows powershell 命令行链接的. 1.打开powershell命令行界面,输入命令[telnet   127 ...

  6. Netty 源码中对 Redis 协议的实现

    原文地址: haifeiWu的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 近期一直在做网络协议相关的工作,所以博客也就与之相关的比较多,今天楼主结合 Re ...

  7. mini Redis(项目 二)

    一个仿Redis的内存数据库(主要用来做命令解析)服务端,  客户端使用的开源工具 : https://dom4j.github.io/     github:https://github.com/h ...

  8. Redis【二】 set|get那些事

    redis4.0.9 SET\GET方法 从哪里开始 server.c里面有每个redis命令对应的执行方法 如 struct redisCommand redisCommandTable[] = { ...

  9. CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍

    前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: 3 ...

随机推荐

  1. 莫名其妙的Explain Plan

    两张表的建表语句: CREATE TABLE hy_emp ( empno NUMBER(8,0) not null primary key, ename NVARCHAR2(60) not null ...

  2. 采用GitOps的11大原因

    Kubernetes允许我们单纯地使用声明性的配置文件来管理我们的应用部署和其他基础设施组件(例如,我们现在都是YAML开发者).这使我们能够把所有这些文件放到Git仓库中,然后把它挂到流水线上(Je ...

  3. java线程的3种实现方式及线程池

    1 准备数据 1.1 目标 为了形象地演示线程的工作现象, 准备两个文件datas/odds.txt和datas/evens.txt, 分别存储奇数和偶数, 内容如下: odds.txt 1 3 5 ...

  4. 在腾讯云云函数计算上部署.NET Core 3.1

    云厂商(腾讯云.Azure等)提供了Serverless服务,借助于Serverless,开发人员可以更加专注于代码的开发,减少运维的成本.腾讯云的函数计算提供了很多运行库,对.NET的支持需要通过c ...

  5. Python3实现zip分卷压缩

    Python实现zip分卷压缩 使用 zipfile 库 查看 官方中文文档 利用 Python 压缩 ZIP 文件,我们第一反应是使用 zipfile 库,然而,它的官方文档中却明确标注" ...

  6. 在express中使用ES7装饰器构建路由

    在Java的Spring框架中,我们经常会看到类似于@Controller这样的注解,这类代码能够极大的提高我们代码的可读性和复用性.而在Javascript的ES7提案中,有一种新的语法叫做deco ...

  7. 【吴恩达课程使用】keras cpu版安装【接】- anaconda (python 3.7) win10安装 tensorflow 1.8 cpu版

    一.确认tensorflow的版本: 接上一条tensorflow的安装,注意版本不匹配会出现很多问题!:[吴恩达课程使用]anaconda (python 3.7) win10安装 tensorfl ...

  8. Java源码之HashMap的hash篇

    提到哈希,我们脑袋中立马就会闪过一个方法,就是hashCode(),没错.就是这个! 我们知道HashMap是通过 数组+链表 的结构进行数据存储的,有数组就会有索引,而HashMap内的数据要存储在 ...

  9. Docker:三、深入Docker容器&Asp.net发版

    各位看官,我们前面已经有过两篇文章的研究了,哈哈,都是皮毛... 今天我们来看看docker容器内部,一探究竟... 一.进入docker容器 进入Linux容器非常简单,直接使用如下命令即可: do ...

  10. MVC设计模式-笔记1

    MVC不仅仅是一个设计模式,它应该说是一种软件开发架构模式,它包含了很多的设计模式,最为密切是以下三种模式: 1.Observer观察者模式 2.Composite组合模式 3.Strategy策略模 ...