当我们想实现一个 redis server,首先要了解 redis 的通信协议。
redis 作者认为数据库系统的瓶颈一般不在于网络流量上,所以使用了一个简单的纯文本的通信协议,叫做 RESP(Redis Serialization Protocol)。
RESP 是 Redis 序列化协议的简写。它是一种直观的文本协议,优势在于实现异常简单,解析性能极好。
RESP 定义了五种类型的数据结构,每个最小单元之间用\r\n
隔开。
+
字符开头,后跟字符串本体。-
字符开头,后跟错误消息本体。:
字符开头,后跟整数的字符串形式。$
字符开头,后跟字符串长度;后面再跟字符串本体。*
字符开头,后跟数组的长度;后面再跟数组元素。如简单字符串:
"+OK\r\n"
错误消息:
"-Error message\r\n"
整数:
":1000\r\n"
":0\r\n"
复杂字符串:
"$6\r\nfoobar\r\n"
"$0\r\n\r\n"
"$-1\r\n" // 长度为-1 的是 null
数组:
"*0\r\n"
"*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"
"*-1\r\n" // 长度为-1 的是 null。比如 blpop 超时时应该返回它,客户端就应该展示为 null。
数组可以是混合类型,如数组的数组,把他们每行分隔开展示如下:
*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 非常简单,协议的定义十分有限。所以要实现一个 redis server,我们可以按行读取客户端的请求数据,同时按行解析它们。
我们可以按照客户端的行数据到达的不同状态画出一个状态机。
从 S 状态(开始状态)开始,如果接受到的字符串时+
或-
或:
,则是三种最简单的一行数据结构:简单字符串、错误消息、整数。那么进入 A 状态,接下来接收到任意值,就进入 END 状态,代表一条请求接收完毕。
如果接收到的是$
符号,那么进入 B 状态。接下来接收到-1
则为 null,接收结束;接收到的是 0 或正整数,则进入 C 状态,此时还会接收到任意一行数据,就进入 END 状态代表结束。
如果接收到*
号,代表数组,这时情况稍微复杂一点。先进入 D 状态,此时接受到的是0
和-1
的话都不会有一个新行,请求结束;此时接收到一个自然数值 n 的话,则代表数组长度为 n,则进入 En 状态,重新开始一个 S 状态的状态机,直到该状态机到 END 状态,n 可以自减 1 ;当 n 为 0 时,直接成为 END 状态。
RESP 的状态机实现部分略长,限于文章篇幅已经放在 GitHub:
https://github.com/sljeff/python-redis-server/blob/master/resp.py
redis server 的实现直接使用 Python 自带的 TCPServer:
from socketserver import TCPServer, StreamRequestHandler
from resp import handle_line
class RedisHandler(StreamRequestHandler):
def handle(self):
state = None
while True:
data = self.rfile.readline().strip()
print(data)
state = handle_line(data, state)
if state.is_stoped:
break
self.wfile.write(b'+OK\r\n')
print('end')
if __name__ == '__main__':
host, port = 'localhost', 6379
with TCPServer((host, port), RedisHandler) as server:
server.serve_forever()
现在不管什么请求到达,server 都会响应+OK\r\n
给客户端。我们的 server 端还会打印出请求的每行字符。
直接敲入python redis_server.py
运行代码;安装 redis-cli 作为客户端。
当敲入redis-cli
时,可以看到客户端发送了一个长度为 1 的数组过来,里面只有一个字符串为COMMAND
:
我们在redis-cli
敲入命令get a
,客户端将get
和a
作为长度为 2 的数组发送到 server。当然,我们的 server 现在还只能响应OK
给客户端。
这个系列可能还会继续更新下去……
项目地址: https://github.com/sljeff/python-redis-server
欢迎 star
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.