Redis是開源的、基於記憶體的資料結構儲存系統,可用作資料庫、快取以及訊息代理方面。Redis支援許多種資料結構,並內建了豐富的諸如冗餘、指令碼、事務、持久化等功能,深受業界喜愛,被各種業務系統廣泛使用。為了方便使用,Redis官網推薦了針對各種程式語言的多種客戶端,支援java、c#、python、c++等主流程式語言。那麼大家會問,既然Redis客戶端已經這麼豐富了,為什麼還要嘗試自己編寫客戶端?我的看法是,知己知彼,自己嘗試製作Redis客戶端,不僅可以加深對Redis的瞭解,而且可以通曉Redis客戶端的原理,為今後的更好地使用、乃至定製改造Redis作好充分準備。
知識準備
要想親自開發Redis客戶端,需要以下知識:
1、網路程式設計基礎
2、熟悉Redis協議
3、瞭解Redis的基本操作
另外文中的例子將會採用java編寫,因此最好有基本的java程式設計知識。
面向讀者
本文面向Redis各層次使用者。
Redis Protocal
Redis協議被稱為:RESP (REdis Serialization Protocol),客戶端通過TCP協議連線到客戶端的6379埠(預設埠)。
RESP協議是在Redis1.2中引入的,不過現在已經是Redis2.0中的標準協議了。所以你應該再Redis客戶端中實現這個協議。
RESP描述
RESP其實是一個序列化協議,支援簡單字串、錯誤、整數、整塊字串和陣列。資料型別依賴頭文字,分別表示如下:
簡單字串的頭文字是“+”
錯誤的頭文字是“-”
整數的頭文字是“:”
整塊字串的頭文字是“$”
陣列的頭文字是“*”
RESP在請求-響應模型中的用法
-
客戶端向Redis伺服器傳送命令,以RESP整塊字串陣列的形式。
-
伺服器端根據命令的結果,選擇適宜的一種RESP型別返回
簡單字串
簡單字串是以半形加號開頭,後跟隨著不含回車換行的字串,然後以回車換行結尾。
舉例如下:+OK
簡單字串是非二進位制安全的,如果需要二進位制安全,可使用“整塊字串”。
錯誤
錯誤和簡單字串類似,但頭文字換成半形減號了。後面跟隨的文字,可以視為錯誤訊息內容。
舉例如下:
-ERR unknown command `foobar`
-WRONGTYPE Operation against a key holding the wrong kind of value
整數
整數與簡單字串類似,頭文字為半形冒號。
舉例如下:
:0
:1000
整塊字串
整塊字串可以用來標示二進位制安全的、最大512MB長度的字串。它以$符號開頭,後跟隨實際字串長度,以回車換行結尾,後跟隨實際字串,再最終以回車換行結尾。
舉例如下:
$6
foobar
空字串表現形式如下:$0
nil表現形式如下:$-1
陣列
陣列以半形星號開頭,後接陣列中元素個數,然後以回車換行結尾,然後後接各個元素。
舉例如下:
空陣列:*0
包含兩個整塊字串的陣列:*2
$3
foo
$3
bar
包含三個整數的陣列:*3
:1
:2
:3
陣列還支援巢狀。
Redis客戶端原理
要實現和Redis服務端通訊,首先需要與Redis服務端建立TCP通訊連線,然後使用上述的RESP協議,將想要執行的Redis命令傳送至服務端,並等待服務端響應,然後接收到響應結果,展示給使用者。
以下程式碼實現了一個簡單的獲取info的操作。
package my_redis_client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.CharBuffer;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
//定義redis服務端預設埠
int port = 6379;
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
//建立tcp連線
socket = new Socket("localhost", port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
//傳送info命令
//客戶端向Redis伺服器傳送命令,以RESP整塊字串陣列的形式
out.println("*1
$4
info
");
System.out.println("Redis command wat sent successfully.");
//接收伺服器的回覆
CharBuffer response = CharBuffer.allocate(1024);
int readedLen = in.read(response);
String responseBody = response.flip().toString();
//輸出伺服器的回覆
System.out.println(responseBody);
}
catch(Exception e) {
e.printStackTrace();
}
finally {
//最後關閉相關的流
if (out != null){
out.close();
out = null;
}
if (in != null) {
try {
in.close();
}
catch(IOException e){
e.printStackTrace();
}
in = null;
}
if (socket != null) {
try {
socket.close();
}
catch(IOException e){
e.printStackTrace();
}
socket = null;
}
}
}
}
執行後,系統將會在命令列介面輸出info的執行結果。
結尾
根據上述程式碼所描述的方法,就可以繼續擴充套件客戶端的功能,實現Redis各種命令了。
筆者實現
原始碼請參考
https://github.com/yourcaptai…
maven中央倉庫
<dependency>
<groupId>net.yesdata</groupId>
<artifactId>dudu-RESP-interpreter</artifactId>
<version>1.0.4</version>
</dependency>