一、RESP协议
1、定义
Redis是一个cs架构的软件,通信一般分两步:
- 客户端client向服务端server发送一条命令
- 服务端解析并执行命令,返回响应结果给客户端
因此客户端发送命令的格式、服务端响应结果的格式必须有一个规范,这个规范就是通信协议。
而在Redis采用RESP协议:
- Redis1.2版本引入RESP协议
- Redis2.0版本中成为与edis服务通信的标准,称为RESP2
- Redis6.0版本中,从RESP2升级到RESP3,增加了更多数据类型并且支持6.0新特性–客户端缓存
2、数据类型
RESP中,通过首字节的字符来区分不同数据类型,常用的有5种:
数据类型 | 说明 | 示例 |
---|---|---|
单行字符串 | 首字节是‘+’,后面跟单行字符串,以CRLF(\r\n)结尾 | 返回OK: “+OK \r\n” |
错误(Errors) | 首字节是‘-’,同上,以CRLF(\r\n)结尾 | “-Errors message \r\n” |
数值 | 首字节是‘:’,后面跟上数字格式的字符串,以CRLF(\r\n)结尾 | “:10 \r\n” |
多行字符串 | 首字节是‘$’,表示二进制安全的字符串,最大支持512MB | “$5\r\nhello\r\n” |
数组 | 首字节是‘*’,后面跟上数组元素个数,再跟上元素,元素数据类型不限 | 见下图 |
二、模拟Redis客户端
public class Main {
static Socket socket;
static PrintWriter writer;
static BufferedReader reader;
public static void main(String[] args) {
try {
//1.建立连接
String host = "127.0.0.1";
int port = 6379;
socket = new Socket(host, port);
//2.获取流
writer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
//3、发送请求
sendRequest("set", "name", "xiaohong");
//4、解析响应
Object obj = handleRepose();
System.out.println(obj);
//5、发送请求
sendRequest("get", "name");
//6、解析响应
Object obj2 = handleRepose();
System.out.println(obj2);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// set name allen
private static void sendRequest(String ...args) {
writer.println("*" + args.length);
for (String str: args) {
writer.println("$" + str.getBytes(StandardCharsets.UTF_8).length);
writer.println(str);
}
writer.flush();
}
private static Object handleRepose() {
try {
int prefix = reader.read();
if (prefix == '+') {
return reader.readLine();
} else if (prefix == '-') {
throw new RuntimeException(reader.readLine());
} else if (prefix == ':') {
return Long.valueOf(reader.readLine());
} else if (prefix == '$') {
int len = Integer.parseInt(reader.readLine());
if (len == -1) {
return null;
}
if (len == 0) {
return "";
}
return reader.readLine();
} else if (prefix == '*') {
return readBulkString();
} else {
throw new RuntimeException("错误格式");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static Object readBulkString() throws IOException {
int len = Integer.parseInt(reader.readLine());
if (len <= 0) {
return null;
}
// 定义集合,接收多个元素
List<Object> list = new ArrayList<>(len);
// 遍历,依次读取每个元素
for (int i = 0; i < len; i++) {
list.add(handleRepose());
}
return list;
}