Redis principle - communication protocol RESP

The address of the original text is updated, and the reading effect is better!

Redis Principle - Communication Protocol RESP | CoderMast Programming Mast icon-default.png?t=N5K3https://www.codermast.com/database/redis/redis-communication-protocol.html

RESP agreement

Redis is a CS architecture software, and the communication is generally divided into two steps (excluding pipeline and PubSub):

  1. The client (client) sends a command to the server (server)
  2. The server parses and executes the command, and returns the response result to the client

Therefore, there must be a specification for the format of the command sent by the client and the response result of the server. This specification is the communication protocol.

CS architecture

CS architecture generally refers to server-client. Server-client, namely Client-Server (C/S) structure. The C/S structure usually adopts a two-tier structure. The server is responsible for data management, and the client is responsible for completing the interactive tasks with users.

Baidu Encyclopedia-CS Architectureopen in new window

In Redis, the RESP (Redis Sericlization Protocol) protocol is used:

  • Redis version 1.2 introduced the RESP protocol
  • In Redis 2.0 version, it became the standard for communicating with Redis server and became RESP 2
  • In Resis version 6.0, the protocol was upgraded from RESP2 to RESP3, adding more data types and supporting the new feature of 6.0 - client cache

But at present, the RESP2 protocol is used by default, which is also the protocol we need to focus on.

In RESP, the characters of the first byte are used to distinguish different data types. The commonly used data types include 5 types:

  • Single-line string: the first byte is '+', followed by a single-line string, ending with CRLF ("\r\n"), for example, return "OK": "+OK\r\n"
  • Errors: the first byte is '-', the same as the single-line string format, except that the string is an exception message, for example: "-Error message\r\n"
  • Value: The first byte is ':', followed by a string of numeric formats, ending with CRLF. For example: ":10\r\n"
  • Multi-line string: the first byte is '$', which means a binary safe string, and the maximum support is 512 MB
    • If the size is 0, it represents an empty string: "$0\r\n\r\n"
    • If the size is -1, it means it does not exist: "$-1\r\n"

This method achieves the purpose of storing strings of special characters by recording the length of the string. For example, to store  helloa string, the underlying storage is: "$5\r\nhello\r\n"

  • Array: The first byte is '*', followed by the number of elements in the array, followed by elements. The data type of the element is not limited, it can be all the above types, and it can also be an array. For example:

*4\r\n
$3\r\nset\r\n
$4\r\nname\r\n
$6\r\n小鹏\r\n
*2\r\n$3\r\nage\r\n:10\r\n

Chinese characters, each occupying 3 bytes.

#custom client

Customize Redis client based on Socket.

class MyRedisClient{
    static Socket s;
    static PrintWriter writer;
    static BufferedReader reader;
    public static void main(String[] args) {
        try {
            // 1.建立连接
            String host = "127.0.0.1";
            int port = 6379;
            s = new Socket(host, port);
            // 2.获取输出流、输入流
            writer = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8));
            reader = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));

            // 3.发出请求
            // 3.1.获取授权 auth codermast
            sendRequest("auth", "codermast");
            Object obj = handleResponse();
            System.out.println("obj = " + obj);

            // 3.2.set name 小鹏
            sendRequest("set", "name", "小鹏");
            // 4.解析响应
            obj = handleResponse();
            System.out.println("obj = " + obj);

            // 3.2.set name 小鹏
            sendRequest("get", "name");
            // 4.解析响应
            obj = handleResponse();
            System.out.println("obj = " + obj);

            // 3.2.set name 小鹏
            sendRequest("mget", "name", "num", "msg");
            // 4.解析响应
            obj = handleResponse();
            System.out.println("obj = " + obj);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5.释放连接
            try {
                if (reader != null) reader.close();
                if (writer != null) writer.close();
                if (s != null) s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static Object handleResponse() throws IOException {
        // 读取首字节
        int prefix = reader.read();
        // 判断数据类型标示
        switch (prefix) {
            case '+': // 单行字符串,直接读一行
                return reader.readLine();
            case '-': // 异常,也读一行
                throw new RuntimeException(reader.readLine());
            case ':': // 数字
                return Long.parseLong(reader.readLine());
            case '$': // 多行字符串
                // 先读长度
                int len = Integer.parseInt(reader.readLine());
                if (len == -1) {
                    return null;
                }
                if (len == 0) {
                    return "";
                }
                // 再读数据,读len个字节。我们假设没有特殊字符,所以读一行(简化)
                return reader.readLine();
            case '*':
                return readBulkString();
            default:
                throw new RuntimeException("错误的数据格式!");
        }
    }

    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(handleResponse());
        }
        return list;
    }

    // set name 小鹏
    private static void sendRequest(String ... args) {
        writer.println("*" + args.length);
        for (String arg : args) {
            writer.println("$" + arg.getBytes(StandardCharsets.UTF_8).length);
            writer.println(arg);
        }
        writer.flush();
    }
}

Guess you like

Origin blog.csdn.net/qq_33685334/article/details/131345688