Redis是用单线程来处理多个客户端的访问,因此作为Redis的开发和运维人员需要了解Redis服务端和客户
端的通信协议,以及主流编程语言的Redis客户端使用方法,同时还需要了解客户端管理的相应API以及开发
运维中可能遇到的问题。本章内容如下:
- 客户端通信协议
- Java客户端Jedis
- Python客户端redis-py
- 客户端管理
- 客户端常见异常
- 客户端案例分析
客户端通信协议
几乎所有的主流编程语言都有Redis的客户端,不考虑Redis非常流行的原因,如果站在技术的角度看原因还
有两个:第一、客户端与服务端之间的通信协议是在TCP协议之上构建的。第二,Redis制定了RESP
(Redis Serialization Protocol,Redis序列化协议)实现客户端与服务端的正常交互,这种协议简单
高效,既能够被机器解析,又容易被人类识别。例如客户端发送一条set hello world命令给服务端,按照
RESP的标准,客户端需要将其封装为如下格式(每行用\r\n间隔):
*3
$3
SET
$5
hello
$5
world
这样Redis服务端能够按照RESP将其解析为set hello world命令,执行后回复的格式如下:
+OK
可以看到除了命令(set hello world)和返回结果(OK)本身还包含了一些特殊字符以及数字,下面将对
这些格式进行说明。
-
发送命令格式
RESP的规定一条命令的格式如下,CRLF代表"\r\n"。
*<参数数量> CRLF $<参数1的字节数量> CRLF <参数1> CRLF ... $<参数N的字节数量> CRLF <参数N> CRLF
依然以set hello world这条命令进行说明。
参数数量为3个,因此第一行为:*3
参数字节数分别是3 5 5,因此后面几行为:
$3 SET $5 hello $5 world
有一点要注意的是,上面只是格式化显示的结果,实际传输格式为如下代码,整个过程如下图所示:
*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n
2019-04-11-21-32-22.png -
返回结果格式
Redis的返回结果类型分为以下五种,如下图所示:
- 状态恢复:在RESP中第一个字节为“+”。
- 错误回复:在RESP中第一个字节为“-”。
- 整数回复:在RESP中第一个字节为“:”。
- 字符串回复:在RESP中第一个字节为“$”。
- 多条字符串回复:在RESP中第一个字节为“*”。
2019-04-11-21-45-40.png我们知道redis-cli只能看到最终的执行结果,那是因为redis-cli本身就是按照RESP进行结果解析
的,所以看不到中间结果,redis-cli.c源码命令结果的解析结构如下:static sds cliFormatReplyTTY(redisReply *r, char *prefix){ sds out = sdsempty(); switch (r->type){ case REDIS_REPLY_ERROR: //处理错误回复 case REDIS_REPLY_STATUS: //处理状态回复 case REDIS_REPLY_INTEGER: //处理整数回复 case REDIS_REPLY_STRING: //处理字符串回复 case REDIS_REPLY_NIL: //处理空 case REDIS_REPLY_ARRAY: //处理多条字符串回复 return out; } }
例如执行set hello world,返回结果是OK,并不能看到加号:
127.0.0.1:6379> set hello world OK
为了看到Redis服务端返回的“真正”结果,可以使用nc命令、telnet命令、甚至写一个socket程序进行
模拟。下面以nc命令进行演示,首先使用nc 127.0.0.1:6379连接到Redis:nc 127.0.0.1:6379
状态回复:set hello world的返回结果为+OK:
set hello world +OK
错误回复:由于sethx这条命令不存在,那么返回结果就是"-"号加上错误消息:
sethx -ERR unknown command 'sethx'
整数回复:当命令的执行结果是整数时,返回结果就是整数回复,例如incr、exsists、del、dbsize
返回结果是整数,例如执行incr counter返回结果就是":"加上整数:incr counter :1
字符串回复:当命令的执行结果是字符串是,返回结果就是字符串回复。例如get、hget返回结果都是
字符串,例如get hello的结果为"$5\r\nworld\r\n":get hello $5 world
多条字符串回复:当命令的执行结果是多条字符串是,返回结果就是多条字符串回复。
例如mget、hgetall、lrange等命令会返回多个结果,例如下面操作:
首先使用mset设置多个键值对:mset java jedis python redis-py +OK
然后执行mget命令返回多个结果,第一个*2代表返回结果的个数,后面的格式是和字符串回复一致的:
mget java python *2 $5 jedis $8 redis-py
有一点需要注意,无论是字符串回复还是多条字符串回复,如果有nil值,那么会返回$-1。
例如,对一个不存在的键执行get操作,返回结果为:
get not_exist_key $-1
如果批量操作中包含一条为nil值的结果,那么返回结果如下:
mget hello not_exists_key java *3 $5 world $-1 $5 jedis
有了RESP提供的发送命令和返回结果的协议格式,各种编程语言就可以利用其来实现相应的Redis客服
端