手把手教你編寫入門級redis客戶端

執著的慢行者發表於2016-12-16

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>

maven中央倉庫地址

https://oss.sonatype.org

相關文章