MongoDB的網絡協議

這篇文章主要來說明MongoDB的網絡協議,總結性的說MongoDB通訊基于TCP之上,數據采用BSON封裝。

關于TCP

TCP具有良好的擁塞控制,可靠傳輸等特性,比較適合數據庫產品的通訊協議。一些對數據一致性,可靠性要求不高的產品也有采用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對應到相應的上下文。

參考引用
關于作者:

楊成虎,阿里巴巴集團技術專家,擅長通過NoSQL存儲系統、Cache系統去解決海量數據的互聯網問題。2009年加入阿里巴巴,先后開發了阿里的小文件系統,KV存儲系統,負責阿里Tair系統的開發與架構設計。2013年至今主導研發了阿里云分布式緩存服務OCS,目前仍致力于NoSQL產品的云服務化工作。

MongoDB的網絡協議》有2個想法

  1. 您好,我是一個mongdodb的初學者,最近在做網絡協議的解析,想請教一個問題,我做一個代理服務器代理了mongodb,期間接收過來的數據包解析出來的opCode如果是通過可視化軟件連接的解析出來都是query,也就是操作碼2004, 如果通過命令行鏈接 都是操作碼2013,不管輸入什么指令,刪除,新增,查詢也好,這個操作碼都是不會變的,但是他數據包中還是會根據指令變換,有insert,drop等。還望解答,十分感謝。

發表評論