1. RESP 协议
Redis是一个CS架构的软件,通信一般分两步(不包括pipeline和PubSub):
- 客户端(client)向服务端(server)发送一条命令
- 服务端解析并执行命令,返回响应结果给客户端
客户端发送命令的格式、服务端响应结果的格式必须有一个规范,这个规范就是通信协议。
在Redis中采用的是RESP(Redis Serialization Protocol)协议:
- Redis 1.2版本引入了RESP协议
- Redis 2.0版本中成为与Redis服务端通信的标准,称为RESP2
- Redis 6.0版本中,从RESP2升级到了RESP3协议,增加了更多数据类型并且支持6.0的新特性–客户端缓存
目前,默认使用的依然是RESP2协议,也是我们要学习的协议版本(以下简称RESP)。
在RESP中,通过首字节的字符来区分不同数据类型,常用的数据类型包括5种:
- 单行字符串:首字节是 ‘+’ ,后面跟上单行字符串,以CRLF( “\r\n” )结尾(二进制不安全:字符串本身不能带 CRLF)。例如返回"OK": “+OK\r\n”
- 错误(Errors):首字节是 ‘-’ ,与单行字符串格式一样,只是字符串是异常信息。例如:"-Error message\r\n"
- 数值:首字节是 ‘:’ ,后面跟上数字格式的字符串,以CRLF结尾。例如:":10\r\n"
- 多行字符串:首字节是 ‘$’,后面跟字节数,CRLF 开始和结束,表示二进制安全的字符串,最大支持512MB:
- 如果大小为0,则代表空字符串:"$0\r\n\r\n"
- 如果大小为-1,则代表不存在:"$-1\r\n"
- 数组:首字节是 ‘*’,后面跟上数组元素个数,再跟上元素,CRLF结尾,元素数据类型不限。
2. 基于 python socket 模拟客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
| import socket
class RedisClient:
def __init__(self):
self.sock = socket.socket()
self.socket_writer = self.sock.makefile(mode='wb') # makefile() 返回 file object 来操作 socket
self.socket_reader = self.sock.makefile(mode='rb')
def send_request(self, *args):
"""
发送命令
:param args:
:return:
"""
self.socket_writer.write(('*' + str(len(args)) + '\r\n').encode())
for command in args:
self.socket_writer.write(('$' + str(len(command.encode())) + '\r\n' + command + '\r\n').encode())
self.socket_writer.flush()
def handle_response(self):
"""
处理响应
:return:
"""
prefix = self.socket_reader.read(1)
if prefix == b'+':
return self.socket_reader.readline().decode()
if prefix == b'-':
raise RuntimeError(self.socket_reader.readline().decode())
if prefix == b':':
return int(self.socket_reader.readline().decode())
if prefix == b'$':
length = int(self.socket_reader.readline().decode())
if length == -1:
return None
if length == 0:
return ''
data = self.socket_reader.read(length).decode()
self.socket_reader.read(2) # 读取剩下的 \r\n
return data
if prefix == b'*':
return self.read_bulk_string()
raise RuntimeError('错误的数据格式')
def read_bulk_string(self):
"""
读数组响应
:return:
"""
resp = []
# 获取数组大小
length = int(self.socket_reader.readline().decode())
if length <= 0:
return None
# 遍历读取元素
for i in range(length):
resp.append(self.handle_response())
return resp
def main(self):
try:
# 1. 建立连接
self.sock.connect(('192.168.31.57', 6379))
# 2. 发出请求
self.send_request('set', 'name', '虎哥')
# 3. 解析响应
obj = self.handle_response()
print(obj)
# 2. 发出请求
self.send_request('get', 'name')
# 3. 解析响应
obj = self.handle_response()
print(obj)
# 2. 发出请求
self.send_request('set', 'name', 'bob')
# 3. 解析响应
obj = self.handle_response()
print(obj)
# 2. 发出请求
self.send_request('get', 'name')
# 3. 解析响应
obj = self.handle_response()
print(obj)
finally:
# 4. 释放连接
self.sock.close()
if __name__ == '__main__':
c = RedisClient()
c.main()
# stdout
> OK
>
> 虎哥
> OK
>
> bob
|