之前只會用redis -cli客戶端, 或者在java程序裏使用jedis來做爲介質與redis服務器通訊.這兩天正好在實習中接觸了一點這方面的內容. 五一放假就稍微翻了翻redis相關資料.html
本篇博客一來是給本身整理筆記,二來是給學校的同窗們分享: 面向於使用過redis, 但只是停留在基本set get的命令, 不瞭解其中原理的同窗.java
首先介紹一下ping pong.. (既然有同窗不懂..我就再加上ping pong的介紹吧)python
登陸redis cli客戶端後, 輸入ping, 服務器會返回pong, 來表示鏈接情況是無缺的, 也表示了服務器大致上是正常運轉的.redis
其中的第一行是我用docker 啓動的客戶端, 你們若是不是docker的話, 本身正常啓動redis -cli就行..docker
ping以後就會收到pong數組
(你們問了docker怎麼用...我這裏附上一個地址...http://www.runoob.com/docker/docker-install-redis.html )服務器
抄代碼的時候你們良心一點...不要去用個人ip去試...(非要拿個人試也不要緊...由於我已經偷偷改掉一位數了)socket
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.210", 6379); // oi流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); // 向redis服務器寫 os.write("PING\r\n".getBytes()); //從redis服務器讀,到bytes中 byte[] bytes = new byte[1024]; int len = is.read(bytes); // to string 輸出一下 System.out.println(new String(bytes,0,len)); }
返回的結果以下:spa
問: 爲何會有一個 '+'符號 呢? redis -cli裏是沒有這個加號的呀? 答:這個和通訊協議有關, 一下子再介紹具體的含義. 不過redis -cli只是把這個'+'符號吞掉處理了, 沒顯示出來罷了. 若是這麼說還不理解的話......看下面代碼...3d
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.210", 6379); // oi流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); // 向redis服務器寫 os.write("PING\r\n".getBytes()); //從redis服務器讀,到bytes中 byte[] bytes = new byte[1024]; if(is.read()=='+'){ // to string 輸出一下 int len = is.read(bytes); System.out.println(new String(bytes,0,len)); } // else if $ // else if * // else }
這樣就跟redis -cli裏的同樣啦.就只是pong了
ps: 不是我逗你們玩....jedis在在協議層也是相似於這樣的寫法, 把 $ * + 這幾個符號挨個判斷來肯定傳輸內容的含義的...
我們先實現, 稍後再講解其中的協議內容
set:
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.210", 6379); // oi流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); // 向redis服務器寫 os.write("set hello world123\r\n".getBytes()); //從redis服務器讀,到bytes中 byte[] bytes = new byte[1024]; int len = is.read(bytes); // to string 輸出一下 System.out.println(new String(bytes,0,len)); }
get:
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.310", 6379); // oi流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); // 向redis服務器寫 os.write("get hello\r\n".getBytes()); //從redis服務器讀,到bytes中 byte[] bytes = new byte[1024]; int len = is.read(bytes); // to string 輸出一下 System.out.println(new String(bytes,0,len)); }
ps:本文只是一個博客,並不是官方文檔, 因此不會一會兒拋出不少概念, 只是一點點引導着同窗們看, 看的着急的同窗能夠直接去看官方文檔
https://redis.io/topics/protocol , 這裏還有個中文版: http://doc.redisfans.com/topic/protocol.html
加號'+' 是來表示狀態回覆的, 在redis服務端向客戶端返回狀態信息時, 就會先發送一個`+`符號來開頭.
接下來是相應的狀態信息, 例如'OK'什麼的.
最後, 要以'\r\n' 來結尾... 我們看一下代碼就明白了
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.210", 6379); // oi流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); // 向redis服務器寫 os.write("set hello world123\r\n".getBytes()); //從redis服務器讀,到bytes中 byte[] bytes = new byte[1024]; if (is.read() == '+') { System.out.println("這是一個狀態回覆哦! 怎麼知道的呢? `+` 號就表示 '狀態回覆' 了"); int len = is.read(bytes); System.out.println("回覆的狀態是: " + new String(bytes, 0, len)); } // 你們想不想看看bytes裏面到底有幾個字符嗎? System.out.println(Arrays.toString(bytes)); // 輸出的是 [79, 75, 13, 10, 0, 0, 0, 0, 0,....] // 其中 79 75 是 `OK` // 其中 13 10 是 `\r\n` // 後面的一串0 是 表示沒有後續內容, 已經讀完. }
$ 表示批量讀取, 通常格式是: $<數字>, 數字來表示正文的內容的字節數
抓包後是這樣的, 客戶端向服務端發送了"get hello", 服務端向客戶端發送了藍色的這兩行.
public static void main(String[] args) throws Exception { // socket Socket socket = new Socket("140.143.135.210", 6379); // oi流 OutputStream os = socket.getOutputStream(); // 爲了解析'\r\n'方便, 我就用改成字符流了 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 向redis服務器寫 os.write("get hello\r\n".getBytes()); // 緩衝數組 char[] chars = new char[1024]; //從redis服務器讀,到bytes中 if (br.read() == '$') { System.out.println("這是一個批量回復哦! 怎麼知道的呢? `$` 號就表示 '批量回復' 了"); System.out.println("$ 後面會跟一個數字, 來表示正文內容的大小"); // readLine直接能判斷'\r' '\n' int len = Integer.parseInt(br.readLine()); System.out.println("$後面跟着的數字是: " + len + ", 表示正文是" + len + "個字節, 接下來只要讀取" + len + "個字節就行了"); // 接下來只讀取len個字符就ok了 (其實單位應該是字節, 可是我中途爲了readLine省事, 改用了字符流, 個數是不變的) br.read(chars, 0, len); System.out.println("get到的結果是: " + new String(chars, 0, len) + ", 數一數真的是" + len + "個字符"); } }
no!!!剛纔客戶端向服務端發送的 "get hello" , 這種只是"內聯命令", 而不是Redis真正的通訊協議.
問: 什麼意思呢? 答: 就是說你能夠像以前那樣給服務端發, 服務器端接受到後, 會遍歷一遍你發送的內容, 最後根據空格來分析你所發的內容的含義.
問: 這樣有什麼很差的嗎? 答: 若是這樣的話, 你就把解析的工做交給了服務器來作, 會加大服務器的工做量.
問: 那怎麼樣纔是符合規範的呢? 符合協議的話真的會提升服務器的效率? 答: 首先看一下符合協議的客戶端和服務端之間的交互把.以下例子:
例: set java python ,抓到包以後是這樣的:
紅色是客戶端發送的內容, 藍色是服務器端返回的內容.
我們一塊兒解析一下:
*3表示 , 客戶端即將發送3段內容
哪三段呢? 第一段: '$3 SET' 第二段: '$4 java' 第三段: '$6 python'
更嚴格地說: 第一段: '$3\r\nSET\r\n' 第二段:'$4\r\njava\r\n' 第三段:'$6\r\npython\r\n'
$符號的意思在上一小節就已經提到過了, 表示下文的內容的長度, 方便服務器進行讀取.
例如: $6就已經把python的長度給彙報出來了, 服務器只須要截取區間[index, index+6]就行了, 不須要去找空格在什麼地方(找空格的時間複雜度是O(n), 而$6這種寫法是O(1) )
其實Jedis作的工做大致就是把SET key value 這樣的格式轉化爲下面這種格式, 而後發到Redis服務端:
*3\r\n $3\r\n SET\r\n $3\r\n key\r\n $5\r\n value\r\n