Redis和MongoDB通信協議簡介

Redis

Redis的通信協議能夠說大集匯了……消息頭標識,消息行還有就行裏可能還有個數據塊大小描述.首先Redis是以行來劃分,每行以\r\n行結束。每一行都有一個消息頭,消息頭共分爲5種分別以下:mongodb

(+) 表示一個正確的狀態信息,具體信息是當前行+後面的字符。數據庫

(-)  表示一個錯誤信息,具體信息是當前行-後面的字符。json

(*) 表示消息體總共有多少行,不包括當前行,*後面是具體的行數。bash

($) 表示下一行數據長度,不包括換行符長度\r\n,$後面則是對應的長度的數據。網絡

(:) 表示返回一個數值,:後面是相應的數字節符。運維

以上就是Redis協議的基礎組成部分,下面來分析幾個指令瞭解一下具體相關指令和返回狀況.ide

SET

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

客戶端

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

客戶端

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

客戶端

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

 

MongDB

總結性的說MongoDB通信基於TCP之上,數據採用BSON封裝

CP具備良好的擁塞控制,可靠傳輸等特性,比較適合數據庫產品的通信協議。一些對數據一致性,可靠性要求不高的產品也有采用UDP協議實現。如Redis,Memcached都支持UDP訪問,但從實際的生產上來講,TCP來的更可靠,UDP的「不可靠」性質,反而會帶來更多的運維負擔,增長了排查問題的複雜性。

關於BSON

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也有很是討厭的一些地方,好比編碼後的數據過大,引入了過的括號,符號等。

Wire Protocol

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對應到相應的上下文。

相關文章
相關標籤/搜索