MongoDB的網絡協議
關於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對應到相應的上下文。
參考引用
最後更新:2017-04-01 13:37:07