Redis的通信協議能夠說大集匯了……消息頭標識,消息行還有就行裏可能還有個數據塊大小描述.首先Redis是以行來劃分,每行以\r\n行結束。每一行都有一個消息頭,消息頭共分爲5種分別以下:mongodb
(+) 表示一個正確的狀態信息,具體信息是當前行+後面的字符。數據庫
(-) 表示一個錯誤信息,具體信息是當前行-後面的字符。json
(*) 表示消息體總共有多少行,不包括當前行,*後面是具體的行數。bash
($) 表示下一行數據長度,不包括換行符長度\r\n,$後面則是對應的長度的數據。網絡
(:) 表示返回一個數值,:後面是相應的數字節符。運維
以上就是Redis協議的基礎組成部分,下面來分析幾個指令瞭解一下具體相關指令和返回狀況.ide
SET HENRY HENRYFAN
以上命令是設置HENRY 的值爲HENRYFAN.在Redis的通信協議上會以空格把命令拆分紅三行;獲得最終的命令以下:函數
*3\r\n $3\r\n SET\r\n $5\r\n HENRY\r\n $8\r\n HENRYFAN\r\n
服務端操做成功this
+OK\r\n
若是出現錯誤服務端會返回編碼
-錯誤信息\r\n
客戶端
GET HENRY
產生的通信指令是:
*2\r\n $3\r\n GET\r\n $5\r\n HENRY\r\n
服務端:
若是存在這個Key則返回
$8\r\n HENRYFAN\r\n
不存在返回
$-1\r\n
客戶端
HKEYS HENRY
以上命令是獲取對應HENRY有多少個field成員
*2\r\n $5\r\n HKEYS\r\n $5\r\n HENRY\r\n
服務端
若是不存在任何字段信息
*0\r\n
若是存在QQ字段信息
*1\r\n $2\r\n QQ\r\n
客戶端
HMGET HENRY QQ
以上命令是獲取HENRY的QQ信息。
*3\r\n $5\r\n HMGET\r\n $5\r\n HENRY\r\n $2\r\n QQ\r\n
服務端
若是不存在字段值
*1\r\n $-1\r\n
存在字段值
*1\r\n $8\r\n 28304340\r\n
總結性的說MongoDB通信基於TCP之上,數據採用BSON封裝
CP具備良好的擁塞控制,可靠傳輸等特性,比較適合數據庫產品的通信協議。一些對數據一致性,可靠性要求不高的產品也有采用UDP協議實現。如Redis,Memcached都支持UDP訪問,但從實際的生產上來講,TCP來的更可靠,UDP的「不可靠」性質,反而會帶來更多的運維負擔,增長了排查問題的複雜性。
BSON做爲JSON的一種擴展,支持了Binary的數據類型,日期數據等。相比較於Protocol Buffers而言,數據是Humman Readable。MongoDB常常說起的Documents,實際上就是BSON格式數據。一樣的,支持嵌套的機制,BSON能夠很好的映射成Object,這相對於表結構,在靈活性上提升了一大截。數據不在是扁平的,能夠是樹形的組織結構,好比:
{ "_id" : 1, "name" : { "first" : "John", "last" : "Backus" }, "contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ], "awards" : [ { "award" : "W.W. McDowell Award", "year" : 1967, "by" : "IEEE Computer Society" }, { "award" : "Draper Prize", "year" : 1993, "by" : "National Academy of Engineering" } ] }
固然BSON也有很是討厭的一些地方,好比編碼後的數據過大,引入了過的括號,符號等。
TCP是一種Stream的通信方式,每次請求之間沒有間隔,數據源源不斷的發來,那如何才能識別出一個完整的請求塊?通常的解決方法是加上一個Header,Header的長度固定,用來描述餘下的信息量,包括攜帶的信息長度。額外說明:MongoDB的網絡協議都是little-endian。
參考util/net/message.h的代碼:
struct Layout { int32_t messageLength; // total message size, including this int32_t requestID; // identifier for this message int32_t responseTo; // requestID from the original request // (used in responses from db) int32_t opCode; };
messageLength表示整個協議的長度,由於是頭部,因此Client每次發送命令時都要先將數據寫到Buffer裏,獲得完整的長度後才能經過TCP發送整個請求。MongoDB規定,messageLength不能大於48MB(1000計算),過大的請求包通常意味着過於複雜的請求類型,或者過大的Document,這與NoSQL的設計原則也是違背的。
requestID/responseTo每一個請求都有一個ID標識,同一時刻不該該出現相同的requestID,Driver和Server經過這個字段來確認是不是同一個請求的上下文
opCode操做代碼,支持的類型:request-opcodes
相關的解析代碼在MessagingPort::recv(Message& m)函數內,首先讀取固定長度的Header(Layout),讀取到Header後,根據messageLength數值作個預判是不是其餘的協議類型,預判所有經過後等待讀取餘下的協議(messageLength-4),若是SocketBuffer中數據不足,就會阻塞在這裏,等待數據包完整到達。
從網絡上得到了完整的數據後交給MyMessageHandler::process來處理接下來的命令,這時opCode開始發揮做用,assembleResponse函數會根據opcode的不一樣,按照不一樣的協議去解析出相應的對象,而後執行命令。最後按照一樣的協議格式發送給Client響應。發給Client的responseTo設置爲與請求命令的requestID相同,以便Driver對應到相應的上下文。