1 个解决方案
redis 客户端和 redis server 之间是通过 RESP(REdis Serialization Protocol)协议来进行通讯的,RESP 协议虽然专为 redis client 和 server 而设计的通讯协议,但是它同样可以被其他项目使用。
RESP 协议有以下优点:
-
1.容易实现
-
2.解析快速
-
3.可读性强
Note: RESP 是 redis client 和 redis server 之间的二进制通讯协议,而 Redis 集群之间并非同样使用这种协议,集群使用不同的二进制协议来交换节点之间的消息。
RESP 协议描述
RESP 协议在 Redis 1.2 中引入,但它已成为在 Redis 2.0 中与 Redis 服务器交谈的标准方式。也是你应该在 Redis 客户端中实现的协议。
RESP 实际上是一个支持以下数据类型的序列化协议:简单 字符串
,错误
,整数
,批量字符串
和 数组
。
RESID 在 Redis 中用作请求 - 响应协议的方式如下:
客户端将命令作为大容量字符串的 RESP 数组发送到 Redis 服务器。服务器根据命令实现回复一个 RESP 类型。 在 RESP 中,某些数据的类型取决于第一个字节:
-
对于简单字符串,第一个字节是“+”
-
对于错误,第一个字节是“ - ”
-
对于整数,第一个字节是“:”
-
对于批量字符串,第一个字节是“$”
-
对于数组,第一个字节是“ *”
此外,RESP 能够使用后面指定的 Bulk Strings 或 Array 的特殊变体来表示空值。
在 RESP 中,协议的不同部分始终以“\ r \ n”(CRLF)结尾。
Jedis 源码分析
我们以 jedis 客户端调用 set 为例:
jedis.set("name", "allen");
跟踪源码:
protected Connection sendCommand(final Command cmd, final byte[]... args) {
try {
connect(); # 获取 socket tcp 连接
Protocol.sendCommand(outputStream, cmd, args); # 发送 RESP 协议命令
pipelinedCommands++;
return this;
} catch (JedisConnectionException ex) {
// ...
}
}
sendCommand 细节:
private static void sendCommand(final RedisOutputStream os, final byte[] command,
final byte[]... args) {
try {
os.write(ASTERISK_BYTE); # 设置 * 开头,后面跟上命令的 length
os.writeIntCrLf(args.length + 1); # 加 1 是命令头的头一个单词,这里是 "set",再加上 crlf 换行符
os.write(DOLLAR_BYTE); # 跟上 $ 符
os.writeIntCrLf(command.length); # 跟上发送命令第一个单词("set")的长度,再加上 crlf 换行符
os.write(command); # 更上具体命令,也就是 "set" 字符串
os.writeCrLf(); # 加上 crlf 换行符
for (final byte[] arg : args) { # 开始处理 key 和 value, 步骤与上面一样
os.write(DOLLAR_BYTE);
os.writeIntCrLf(arg.length);
os.write(arg);
os.writeCrLf();}
} catch (IOException e) {
throw new JedisConnectionException(e);
}
}
上面这一套流程走完,完成 RESP 协议的字符串拼装,得到结果:*3\r\n$3\r\nset$4\r\nname\r\n$5\r\nallen\r\n
,拼装完成后,再以 socket 发送给 redis server.