1、官網文檔
https://redis.io/topics/protocol
http://www.redis.cn/topics/protocol.html
2、協議介紹
redis協議規范(Redis Protocol specification)。
redis協議在以下幾點之間做出了折衷:
(1)簡單的實現
(2)快速地被計算機解析
(3)簡單得可以能被人工解析
(4)網絡層,Redis在TCP端口6379上監聽到來的連接(本質就是socket),客戶端連接到來時,Redis服務器為此創建一個TCP連接。在客戶端與服務器端之間傳輸的每個Redis命令或者數據都以\r\n結尾。
3、請求
Redis接收由不同參數組成的命令。一旦收到命令,將會立刻被處理,並回復給客戶端
4、新的統一請求協議
在這個統一協議里,發送給Redis服務端的所有參數都是二進制安全的。以下是通用形式:
*后面數量表示存在幾個$
$后面數量表示字符串的長度
例子:
*3 $3 SET $5 mykey $7 myvalue
上面的命令看上去像是單引號字符串,所以可以在查詢中看到每個字節的准確值:
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
在Redis的回復中也使用這樣的格式。批量回復時,這種格式用於每個參數$6\r\nmydata\r\n。 實際的統一請求協議是Redis用於返回列表項,並調用 Multi-bulk回復。 僅僅是N個以以*\r\n為前綴的不同批量回復,是緊隨的參數(批量回復)數目。
5、回復
Redis用不同的回復類型回復命令。它可能從服務器發送的第一個字節開始校驗回復類型:
(1)用單行回復,回復的第一個字節將是“+”
(2)錯誤消息,回復的第一個字節將是“-”
(3)整型數字,回復的第一個字節將是“:”
(4)批量回復,回復的第一個字節將是“$”
(5)多個批量回復,回復的第一個字節將是“*”
例子:狀態回復(或者單行回復)
以“+”開始以“\r\n”結尾的單行字符串形式。例如:
"+OK\r\n"
客戶端庫將在“+”后面返回所有數據,正如上例中字符串“OK”一樣。
有關與其他的操作請查看官方文檔。
6、模擬Redis服務和客戶端通訊,實現RESP協議通信
枚舉類
public enum CommandRedis { SET, GET, SETNX }
實現類
package com.protocol; import org.junit.Test; import java.io.IOException; import java.net.Proxy; import java.net.Socket; /** * Created by wxh on 2018/1/29. */ public class RedisClientByResp { private Socket socket; public RedisClientByResp() { try { socket = new Socket("192.168.56.180", 6379); } catch (IOException e) { e.printStackTrace(); System.out.println("連接失敗" + e.getMessage()); } } /** * 設置值 * @param key * @param value * @return * @throws IOException */ public String set(String key, String value) throws IOException { StringBuilder sb = new StringBuilder(); sb.append("*3").append("\r\n"); sb.append("$").append(CommandRedis.SET.name().length()).append("\r\n"); sb.append(CommandRedis.SET.name()).append("\r\n"); // 注意中文漢字。一個漢字兩個字節,長度為2 sb.append("$").append(key.getBytes().length).append("\r\n"); sb.append(key).append("\r\n"); sb.append("$").append(value.getBytes().length).append("\r\n"); sb.append(value).append("\r\n"); System.out.println(sb.toString()); socket.getOutputStream().write(sb.toString().getBytes()); byte[] b = new byte[2048]; socket.getInputStream().read(b); return new String(b); } /** * 獲取值 * @param key * @return * @throws Exception */ public String get(String key) throws Exception { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("*2").append("\r\n"); stringBuffer.append("$").append(CommandRedis.GET.name().length()).append("\r\n"); stringBuffer.append(CommandRedis.GET).append("\r\n"); stringBuffer.append("$").append(key.getBytes().length).append("\r\n"); stringBuffer.append(key).append("\r\n"); socket.getOutputStream().write(stringBuffer.toString().getBytes()); byte[] b = new byte[2048]; socket.getInputStream().read(b); return new String(b); } /** * 設置值:不會覆蓋存在的值 * @param key * @param value * @return * @throws Exception */ public String setnx(String key, String value) throws Exception { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("*3").append("\r\n"); stringBuffer.append("$").append(CommandRedis.SETNX.name().length()).append("\r\n"); stringBuffer.append(CommandRedis.SETNX.name()).append("\r\n"); stringBuffer.append("$").append(key.getBytes().length).append("\r\n"); stringBuffer.append(key).append("\r\n"); stringBuffer.append("$").append(value.getBytes().length).append("\r\n"); stringBuffer.append(value).append("\r\n"); socket.getOutputStream().write(stringBuffer.toString().getBytes()); byte[] b = new byte[2048]; socket.getInputStream().read(b); return new String(b); } public static void main(String[] args) throws Exception { System.out.println(new RedisClientByResp().set("mykey" ,"myvalue")); System.out.println(new RedisClientByResp().get("mykey")); } }