RESP 協議#
Redis 是一個 CS 架構的軟體,通信一般分兩步(不包括 pipeline 和 PubSub)
- 客戶端 client 向服務端 server 發送一條命令
- 服務端解析並執行命令,返回響應結果給客戶端
因此客戶端發送命令的格式、服務端響應結果的格式必須有一個規範,這個規範就是通信協議
RESP:redis serialization protocol
學習 RESP2 協議,在 2.0 版本推出,6.0 升級為 RESP3 但因為變化過大仍默認使用 RESP2 協議
RESP 協議數據類型#
對於單行字符串、錯誤、數據都是二進制不安全的,如果在內容中穿插了 \r\n 可能會誤解成結尾
數組類型看上去其實和 AOF 文件挺像
模擬 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