摘要:在讀者閱讀實現代碼時,也可以看到最新版本,與有一處是在文件清除掉回車換行符該行被暫時注釋掉,也就是在版本,使用作為文本協議分隔符,確定命令的結尾。
寫在前面
本篇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多了如下特性:
多DB,支持數據庫切換;
高可用之數據持久化;
高可用之主從復制;
安全、事務、發布訂閱等。
本文重點實現數據在內存中的存儲及查詢,交互協議和持久化會在后續短文實現。
執行流程 流程拆解從服務端初始化、客戶端輸入“set alpha 123”命令,到接收到返回結果,經歷如下步驟:
實例化server及相關資源,準備連接;
客戶端與服務端建立連接,服務端初始化一個client結構體,用來保存當前連接;
將客戶端請求的“set alpha 123”字符串,分拆為“set”、“alpha”、“123”三部分;
查找是否支持set命令,并確定參數合法,調set用命令的實現函數SetCommand,更新db數據;
執行結果響應給客戶端;
執行流程大致如下:
接下來分開說明主要步驟。
1.數據交互前篇實現的客戶端/服務端交互使用的協議是textproto,沒有使用Redis自身的統一協議。這一篇,客戶端對服務端執行的get、set命令,均以原生文本方式發送給服務端執行。在讀者閱讀實現代碼時,也可以看到最新release版本,與v0.0.1有一處diff是在godis-cli.go文件:
//清除掉回車換行符 //text = strings.Replace(text, " ", "", -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, } } // 初始化db func 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.非法命令:?
服務端啟動:
cli請求:
本篇問題文本協議的格式分隔符沒有處理好,在服務端又是使用的conn.Read(buff),讀入的數據與buff額外的緩沖區混在一起。而print字符串又屏蔽了有效字符串后全零的問題(print調試最好輸出原始數據)。
下集預告1. 實現Redis統一協議
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/28809.html
摘要:在本文,將替換文本協議為版本后的統一協議。協議格式在發送命令和返回結果中均使用同一套標準協議。實現通信協議版本協議實現初探很多相關的組件模塊工具都有協議的生成和解析實現,并歷經生產環境的考驗。 寫在前面 本文實現的Godis代碼版本為:v0.0.3 在前三篇文章中,實現了客戶端/服務端的交互(基于textprotoco)、服務端初始化和get/set命令。如果閱讀過或者調試過粗略的代碼...
摘要:寫在前面在前一篇梳理了版本的基本功能,這一篇要做的是實現客戶端服務端的交互。進入正題事件處理器既要實現交互,網絡編程必不可少。 寫在前面 在前一篇梳理了Godis v1.0版本的基本功能,這一篇要做的是實現客戶端/服務端的交互。先讓代碼跑起來,才算有了生命力。本篇Godis版本號:v0.0.1 在這個系列文章里,盡量減少介紹Golang語法、C語言語法和redis原理,聚焦在用Gol...
閱讀 2259·2021-11-22 09:34
閱讀 2034·2021-09-22 15:22
閱讀 2028·2019-08-29 15:05
閱讀 2120·2019-08-26 10:43
閱讀 3418·2019-08-26 10:26
閱讀 897·2019-08-23 18:29
閱讀 3529·2019-08-23 16:42
閱讀 2007·2019-08-23 14:46