redis 使用的 client/server 架构。c/s 架构下的 client 和 server 第一个要解决的问题就是进程间通信问题。这个的解决方案很多,最常见的当属网络套接字,而且大多数都采用 TCP 协议通信。
建立在 TCP 协议上的应用程序都有自己的应用层协议,常见的比如有 HTTP 协议等。redis 与 client 通信的时候,使用自己定义的 RESP(REdis Serialization Protocol) 协议。这个协议主要优点有:
- 实现简单
- 解析快速
- 人类可读
一个通信协议往往需要在多方面做出取舍,redis 显然有它自己的取舍。值得一提的是,RESP 虽然是一个文本协议,但是其却可以安全的传递二进制数据,因为其每一个数据包都是 length-prefixed 。
网络层
本质上 RESP 并未要求一定构建在 TCP 协议之上,但是 redis 内部实现是在本机的 6379 端口上创建了一个 TCP 监听套接字。
问-答模型
与 http 相似,RESP 协议也是一个问-答模型的通信协议,即由 client 发出请求,每一个请求 server 都会返回响应。不过 redis 在 redis 中存在两个例外:
- redis 支持 pipelining 模式,支持 cilent 一次性发送多条请求,然后等待响应(多条请求,一条响应)
- 如果 client 使用了 Pub/Sub 功能,RESP 协议就变成了一个推送模型,client 等待 redis 发送信息到。
协议描述
RESP 其实本质上是一个数据序列化协议,其支持的类型有:
- 简单字符串,以 “+” 开头
- 错误,以”-“ 开头
- 整数,以":"开头
- 大字符串,以“$“开头
- 数组,以”*“开头
在 client 与 redis 通信的时候,客户端将 commands 按照 RESP 序列化为一个大的数组发送到 redis,redis 会根据对应请求,响应上述的一种基本类型。
RESP 协议中,可以有多个部分内容,就像 http 协议中的 header 和 body 一样,不同部分之间用 \r\n 分割。
简单字符串
简单字符串用于传输 non binary safe 字符串(\0 结尾),按照如下格式编码:
|+|string|\r\n
# example
"+OK\r\n"
要注意字符串中不可用包含 \r\n。
错误
错误的编码格式与简单字符串相比,其实只有一点区别:以 - 打头而已。
|-|error|\r\n
# example
"-Error message\r\n"
整数
|:|number-string|\r\n
# example
":1000\r\n"
大字符串
大字符串的使用场景主要是为了编码一些 binary safe string,最大长度可达 512M。
|$|#data|\r\n|data|\r\n
# example
"$6\r\nfoobar\r\n"
# empty bulk string
"$0\r\n\r\n"
# Null bulk string, 用于表示不存在,注意与 empty 区别
"$-1\r\n"
数组
redis client 在向 redis 发送 commands 时,就是使用数组编码命令,进行发送。
|*|#array|\r\n|element1|element2|...|elementN|
# examples
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
"*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"
# empty array
"*0\r\n"
# null array,注意与 empty array 区分
"*-1\r\n"
数组中的元素可以是基础类型的任意一种,elementN 即为该种类型的序列化表示。
总结
- 作为一个 c/s 架构的服务器,redis 有其自己定义的 RESP 协议
- RESP 协议中有简单字符串、整数、大字符串、错误、数组 5 中数据类型