Chanler

Chanler

「黑馬 Redis 原理」三、通信協議

RESP 協議#

Redis 是一個 CS 架構的軟體,通信一般分兩步(不包括 pipeline 和 PubSub)

  1. 客戶端 client 向服務端 server 發送一條命令
  2. 服務端解析並執行命令,返回響應結果給客戶端

因此客戶端發送命令的格式、服務端響應結果的格式必須有一個規範,這個規範就是通信協議

RESP:redis serialization protocol

學習 RESP2 協議,在 2.0 版本推出,6.0 升級為 RESP3 但因為變化過大仍默認使用 RESP2 協議

RESP 協議數據類型#

對於單行字符串、錯誤、數據都是二進制不安全的,如果在內容中穿插了 \r\n 可能會誤解成結尾

數組類型看上去其實和 AOF 文件挺像

image.png|500

模擬 Redis 客戶端#

跑下來 reader 收不到信息?

public class Main {  
    static Socket s;  
    static PrintWriter out;  
    static BufferedReader in;  
    public static void main(String[] args) {  
        try {  
            // 1. 建立連接  
            String host = "redis.orb.local";  
            int port = 6379;  
            s = new Socket(host, port);  
            // 2. 獲取輸入、輸出流  
            out = new PrintWriter(  
                    new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8));  
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));  
            // 3. 發出請求並解析  
            // 獲取授權 auth TheBestWorkLoanapp1            sendRequest("auth", "TheBestWorkLoanapp1");  
            Object obj = handleResponse();  
            System.out.println(obj.toString());  
            // 設置鍵值對 set name Chanler            sendRequest("set", "name", "Chanler");  
            obj = handleResponse();  
            System.out.println(obj.toString());  
            // 獲取鍵值對 get name            sendRequest("get", "name");  
            obj = handleResponse();  
            System.out.println(obj.toString());  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            // 5. 釋放連接  
            try {  
                if (s != null) {s.close();}  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
            try {  
                if (out != null) {out.close();}  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            try {  
                if (in != null) {in.close();}  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    private static Object handleResponse() throws IOException {  
        // 讀取首字節判斷數據類型  
        int prefix = in.read();  
        switch (prefix) {  
            case '+':  
                return in.readLine();  
            case '-':  
                throw new RuntimeException(in.readLine());  
            case ':':  
                return Long.parseLong(in.readLine());  
            case '$':  
                int len = Integer.parseInt(in.readLine());  
                if (len == -1) {  
                    return null;  
                }  
                if (len == 0) {  
                    return "";  
                }  
                // 這裡應該讀 len 個字節,但這裡已經轉換成了字符流,所以假設不存在特殊字符  
                return in.readLine();  
            case '*':  
                return readBulkString();  
            default:  
                throw new RuntimeException("Invalid prefix");  
        }  
    }  
  
    private static Object readBulkString() throws IOException {  
        int len = Integer.parseInt(in.readLine());  
        if (len == 0) {  
            return null;  
        }  
        List<Object> list = new ArrayList<>();  
        for (int i=0; i<len; i++) {  
            list.add(handleResponse());  
        }  
        return list;  
    }  
  
    // 發送 set name Chanler    
    private static void sendRequest(String ... args) {  
        // \r\n 就是換行符,這裡用 println 直接附帶了  
        out.println("*" + args.length);  
        for (String arg : args) {  
            out.println("$" + arg.getBytes(StandardCharsets.UTF_8).length);  
            out.println(arg);  
        }  
        out.flush();  
    }  
}

此文由 Mix Space 同步更新至 xLog
原始鏈接為 https://blog.0xling.cyou/posts/redis/redis-5


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。