一、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 allenprivate 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;}