用Go實現Redis之三get/set命令實現

來源:互聯網
上載者:User

寫在前面

本篇Godis版本號碼:v0.0.2

前一篇文章實現了用戶端/服務端的互動。這一篇,主要介紹get/set命令的實現。
命令本身比較簡單,支撐命令的整個系統基礎比較麻煩。本文會介紹get/set操作涉及的組件和模組,並適當簡化,最後實現功能。

Redis用C語言寫成,C語言自身不支援複雜資料結構,所以Redis中的string、list、set等結構,均是Redis自身實現;而Go版本的Godis,會盡量使用原生資料結構。

原理簡介

set命令和get命令是Redis中使用頻率最高的命令,以set為例,命令“set key value”,將索引值對儲存到Redis服務端,可以簡化為“操作一個遠程關聯陣列”。
當然,相比關聯陣列,Redis多了如下特性:

  1. 多DB,支援資料庫切換;
  2. 高可用之資料持久化;
  3. 高可用之主從複製;
  4. 安全、事務、發布訂閱等。

本文重點實現資料在記憶體中的儲存及查詢,互動協議和持久化會在後續短文實現。

執行流程

流程拆解

從服務端初始化、用戶端輸入“set alpha 123”命令,到接收到返回結果,經曆如下步驟:

  1. 執行個體化server及相關資源,準備串連;
  2. 用戶端與服務端建立串連,服務端初始化一個client結構體,用來儲存當前串連;
  3. 將用戶端請求的“set alpha 123”字串,分拆為“set”、“alpha”、“123”三部分;
  4. 尋找是否支援set命令,並確定參數合法,調set用命令的實現函數SetCommand,更新db資料;
  5. 執行結果響應給用戶端;

執行流程大致如下:

接下來分開說明主要步驟。

1.資料互動

前篇實現的用戶端/服務端互動使用的協議是textproto,沒有使用Redis自身的統一協議。這一篇,用戶端對服務端執行的get、set命令,均以原生文本方式發送給服務端執行。在讀者閱讀實現代碼時,也可以看到最新release版本,與v0.0.1有一處diff是在godis-cli.go檔案:

//清除掉斷行符號分行符號//text = strings.Replace(text, "\n", "", -1)

該行被暫時注釋掉,也就是在v0.0.2版本,使用“n”作為文本協議分隔字元,確定命令的結尾。

如用戶端發送"set alpha 123",服務端接收到的就是如下位元組資料:

分別對應ASCII碼為:

2.服務端準備

第一篇(https://segmentfault.com/a/11...)提到過服務端需要一個server結構體儲存相關資訊,在服務端準備好處理請求前,對該結構進行執行個體化並進行一系列初始化操作:初始化基本配置、分配多db資源、載入磁碟持久化資料、訊號監聽處理等。
初始化server的代碼主要是一些賦值操作和相應結構體初始化:

// 初始化服務端執行個體func initServer() {    godis.Pid = os.Getpid()    godis.DbNum = 16    initDb()    godis.Start = time.Now().UnixNano() / 1000000    //var getf server.CmdFun    getCommand := &core.GodisCommand{Name: "get", Proc: core.GetCommand}    setCommand := &core.GodisCommand{Name: "set", Proc: core.SetCommand}    godis.Commands = map[string]*core.GodisCommand{        "get": getCommand,        "set": setCommand,    }}// 初始化dbfunc initDb() {    godis.Db = make([]*core.GodisDb, godis.DbNum)    for i := 0; i < godis.DbNum; i++ {        godis.Db[i] = new(core.GodisDb)        godis.Db[i].Dict = make(map[string]*core.GodisObject, 100)    }}

這裡簡單解釋下core.GodisCommand結構。該結構很簡單,記錄了命令的名字、函數指標和參數校正相關的資訊。在執行命令前,校正命令是否存在的過程,需要尋找支援的”命令表“。該命令表就是commands,commands由一組core.GodisCommand構成。commands中查不到的命令,則為不支援的命令;而命令參數需要滿足哪些條件,由core.GodisCommand結構的其他欄位記錄。

3.服務端接收

當服務端準備就緒,開始接受請求。
請求到來,server會執行個體化一個client結構體,儲存當前串連。該client結構體也在前篇有介紹,主要用來儲存當前串連的db等資訊。

// CreateClient 串連建立 建立client記錄當前串連func (s *Server) CreateClient(conn net.Conn) (c *Client) {    c = new(Client)    c.Db = s.Db[0]    c.Argv = make([]*GodisObject, 5)    c.QueryBuf = ""    return c}

4.執行命令

將請求的命令分解,校正無誤後,調用響應函數執行。注意,只在當前client結構指向的db中執行插入、查詢、更新等操作。如果需要操作其他db,執行"select"命令便將當前client指向的db指標指向select後的位置。
執行完成後,將結果寫入到client結構的Buf欄位。

下面的handle函數包括了client的建立、資料接收、執行和返回。

// 處理請求func handle(conn net.Conn) {    c := godis.CreateClient(conn)    for {        err := c.ReadQueryFromClient(conn)        if err != nil {            log.Println("readQueryFromClient err", err)            return        }        c.ProcessInputBuffer()        godis.ProcessCommand(c)        responseConn(conn, c)    }}// ProcessCommand 執行命令func (s *Server) ProcessCommand(c *Client) {    v := c.Argv[0].Ptr    name, ok := v.(string)    if !ok {        log.Println("error cmd")        os.Exit(1)    }    cmd := lookupCommand(name, s)    if cmd != nil {        c.Cmd = cmd        call(c, s)    } else {        addReply(c, CreateObject(ObjectTypeString, fmt.Sprintf("(error) ERR unknown command '%s'", name)))    }}

ProcessCommand函數先從命令表中尋找命令,如果存在,調用該命令的實現,並將結果寫入client.Buf欄位。

5.響應請求

將client.Buf內容,返回給請求方,完成。
最後將執行結果返回給請求方。

// 響應返回給用戶端func responseConn(conn net.Conn, c *core.Client) {    conn.Write([]byte(c.Buf))}

測試

分別編譯服務端和命令列用戶端:
go build godis-server.go
go build godis-server.go

啟動 ./godis-server

1.非法命令:


2.set/get命令:

服務端啟動:

cli請求:

本篇問題

  1. 文本協議的格式分隔字元沒有處理好,在服務端又是使用的conn.Read(buff),讀入的資料與buff額外的緩衝區混在一起。而print字串又屏蔽了有效字串後全零的問題(print調試最好輸出未經處理資料)。

下集預告

  1. 實現redis統一協議
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.