一、简介
MongoDB Wire Protocol是一个简单的基于socket,请求/响应方式的协议,客户端使用常规的TCP/IP套接字(socket)进行通信,服务端默认监听端口是27017,同时也会启动一个基本的HTTP服务器,监听端口为28017,可以通过浏览器http://localhost:28017l来获取数据库的管理信息。MongoDB Wire Protocol 协议中的所有整数都是采用little-endian字节顺序 。
二、BSON
先来了解一下BSON,BSON 是Binary JSON的缩写,是类似JSON文档的二进制编码序列化。与JSON一样,BSON支持在其他文档和数组中嵌入文档和数组。BSON还包含允许表示不属于JSON规范的数据类型的扩展。例如,BSON具有Date类型和BinData类型。
BSON旨在具有以下三个特征:
-
轻量级
将空间开销保持在最低限度对于任何数据表示格式都很重要,尤其是在通过网络使用时。
-
穿越
BSON旨在轻松遍历。这是其作为主要的数据表示角色的重要属性的MongoDB。
-
高效
由于使用C数据类型,在大多数语言中可以非常快速地将数据编码到BSON并从BSON解码。
BSON主要类型和格式:
e_name | ::= | cstring | cstring 类型的 元素名 |
string | ::= | int32 (byte*) "\x00" | int32 四字节表示byte*(0个或者多个字符)+ \x00(一个字节) 的长度。 |
cstring | ::= | (byte*) "\x00" | 以\x00结束的字符串。 |
document | ::= | int32 e_list "\x00" | 文档=文档的长度+元素列表+"\x00" |
e_list | ::= | element e_list | 元素列表=一个元素 + 元素列表 |
| | "" | 或者是空“ ” | |
element | ::= | "\x01" e_name double | “\x01″+元素名+64 位double |
| | "\x02" e_name string | \x02″+元素名+string | |
| | "\x03" e_name document | 嵌入文档(子文档) | |
| | "\x04" e_name document | 数组,一种特殊的子文档 | |
| | "\x05" e_name binary | 二进制数据 | |
| | "\x06" e_name | 未定义 | |
| | "\x07" e_name (byte*12) | 对象ID,即MongoDB中默认的”_id”的类型。12字节。 | |
| | "\x08" e_name "\x00" | Boolean "false" | |
| | "\x08" e_name "\x01" | Boolean "true" | |
| | "\x09" e_name int64 | UTC日期时间 | |
| | "\x0A" e_name | 空值 | |
| | "\x0B" e_name cstring cstring | 正则表达式 - 第一个cstring是正则表达式模式,第二个是正则表达式选项字符串。选项由字符标识,必须按字母顺序存储。有效选项是'i'表示不区分大小写匹配,'m'表示多行匹配,'x'表示详细模式,'l'表示\ w,\ W等等。区域设置依赖,'s'表示dotall模式('。 '匹配一切),'你'使\ w,\ W等匹配unicode。 | |
| | "\x0C" e_name string (byte*12) | 放弃使用 | |
| | "\x0D" e_name string | JavaScript 代码 | |
| | "\x0E" e_name string | 放弃使用 | |
| | "\x0F" e_name code_w_s | JavaScript code w/ scope | |
| | "\x10" e_name int32 | 32位整数 | |
| | "\x11" e_name uint64 | 时间戳 | |
| | "\x12" e_name int64 | 64位整数 | |
| | "\x13" e_name decimal128 | 128位十进制浮点数 | |
| | "\xFF" e_name | 最小键值 | |
| | "\x7F" e_name | 最大键值 |
举例 {"hello": "world"}
BSON格式为 "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"
前面"\x16\x00\x00\x00" : 文档的长度,这里是小端表示,即文档的长度是22个字节,
第5个字节:\x02 : 元素的类型,即"world"的类型是string,string类型 = 长度 + 内容 + '\0',注意,这里指的是"world"的类型。
hello\x00 : 元素的名字,以"\0"结尾,在这里,元素的名字是hello。注意元素的名字是CString类型,不是String类型。即元素的名字是没有长度信息的。
x06\x00\x00\x00 : string类型的长度
world\x00 : string的内容和结尾的'\0'
最后一个"\x00" : 文档的结尾。
{"BSON": ["awesome", 5.05, 1986]} → "1\x00\x00\x00\x04BSON\x00&\x00
\x00\x00\x020\x00\x08\x00\x00
\x00awesome\x00\x011\x00333333
\x14@\x102\x00\xc2\x07\x00\x00
\x00\x00"
1\x00\x00\x00 : 文档的长度
第5个字节\x04 : 元素的类型,\x04,即数组类型
BSON\x00 : 元素的名字,以"\0"结尾,在这里,元素的名字是BSON
&\x00\x00\x00 : 数组即一个子文档,子文档的长度,这里子文档实际上是{"0":"awesome","1":5.05,"2":1986}
\x02 : 元素的类型,即"awesome"的类型是string
0\x00 : 即"0"'\0',即元素的名字是"0",字符串以'\0'结尾
\x08\x00\x00\x00awesome\x00 : 长度 + "awesome" + '\0'
\x01 : 元素的类型,即5.05的类型是Floating point
1\x00 : 即"1"'\0',即元素的名字是"1"
333333\x14@ : 即5.05
\x10 : 元素的类型,即1986的类型是32-bit Integer
2\x00 : 即"2"'\0',即元素的名字是"2"
\xc2\x07\x00\x00 : 即1986
\x00 : 子文档,即数组的结尾
\x00 : 文档的结尾
三、消息格式与数据类型
有两种类型的消息:客户端请求和数据库响应,用C-like struct
来描述消息结构。
1、消息头
每条消息都会包含一个消息头,消息头格式为:
struct MSGHEADER {
int32 messageLength; // 消息长度(字节),包括它自身
int32 requestID; // 消息标识符,用于在请求响应过程中唯一标识该消息
int32 responseTo; // 源请求id(用于服务端发送应答信息时)requestID from the original request
// (used in reponses from db)
int32 opCode; //消息类型,参见enum中描述
};
enum Operations {
opReply = 1, /* 对客户端请求的响应. */
dbMsg = 1000, /* 通常的消息命令(跟着字符串) */
dbUpdate = 2001, /* 更新document消息 */
dbInsert = 2002, /* 插入新document消息*/
//dbGetByOID = 2003,/*保留*/
dbQuery = 2004, /* 查询一个集合*/
dbGetMore = 2005, /* 从(一个)查询中获取更多数据,参见 Cursors */
dbDelete = 2006, /* 删除一个或多个document*/
dbKillCursors = 2007, /* 通知数据库,客户端已执行完毕,可以关闭该Cursors*/
OP_COMMAND = 2010, /*表示命令请求的集群内部协议。已过时,将来会弃用*/
OP_COMMANDREPLY = 2011, /*集群内部协议表示对OP_COMMAND的回复。已过时,将来会弃用*/
OP_MSG = 2013 /*使用MongoDB 3.6中引入的格式发送消息。*/
};
messageLength | 消息的总大小(以字节为单位)。该总数包括保存消息长度的4个字节。 |
requestID | 客户端或数据库生成的标识符,用于唯一标识此消息。对于客户端生成的消息(例如OP_QUERY和 OP_GET_MORE),它将responseTo 在OP_REPY 消息的字段中返回。客户端可以使用requestID 和 responseTo 字段将查询响应与原始查询相关联。 |
responseTo | 对于来自数据库的消息,这将 来自客户端requestID 的OP_QUERY或 OP_GET_MORE消息。客户端可以使用requestID 和responseTo 字段将查询响应与原始查询相关联。 |
opCode | 消息类型。enum中的值。 |
注意:只有客户端使用OP_QUERY 和 OP_GET_MORE消息时才会收到服务端的Reply响应消息,其他请求类型不会收到回复。
2、客户端请求消息
2、1 OP_UPDATE消息用于更新集合中的文档。OP_UPDATE消息的格式如下:
struct OP_UPDATE {
MsgHeader header; // 前面介绍的标准消息类信息
int32 ZERO; // 0 - 为将来使用而保留的数据位
cstring fullCollectionName; // 完整的集合名称,形如:"dbname.collectionname"
int32 flags; // 位向量,参见下面介绍
document selector; // 查询选择器,用于指定查询条件
document update; // 指定要执行的更新(document)
}
header | 消息标头。 |
ZERO | 整数值0.保留供将来使用。 |
fullCollectionName | 完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称. 的串联,使用一个用于连接。例如,对于数据库foo 和集合bar ,完整集合名称为foo.bar 。 |
flags | 位向量,用于指定操作的标志。位值对应于以下内容:
|
selector | BSON文档,指定用于选择要更新的文档的查询。 |
update | BSON文档,指定要执行的更新。 |
OP_UPDATE消息没有响应。
2、2 OP_INSERT
OP_INSERT消息用于将一个或多个文档插入集合中。
struct {
MsgHeader header; // 前面介绍的标准消息类信息
int32 ZERO; // 0 - 为将来使用而保留的数据位
cstring fullCollectionName; // 完整的集合名称,形如:"dbname.collectionname"
document* documents; // 要插入的一个或多个document,如为多个时,这些document会依次逐个写到socket里
}
header | 消息标头。 |
flags | 位向量,用于指定操作的标志。位值对应于以下内容:
|
fullCollectionName | 完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称. 的串联,使用一个用于连接。例如,对于数据库foo 和集合bar ,完整集合名称为foo.bar 。 |
documents | 要插入集合的一个或多个文档。如果有多个,则依次将它们按顺序写入套接字。 |
OP_INSERT消息没有响应。
2、3 OP_QUERY
OP_QUERY消息用于在数据库中查询集合中的文档。
struct OP_QUERY {
MsgHeader header; // 标准消息类信息
int32 flags; // 位向量,参见下面介绍
cstring fullCollectionName; // 完整的集合名称,形如:"dbname.collectionname"
int32 numberToSkip; // 设置从第一个document起,跳过(忽略)的document数
int32 numberToReturn; // 返回的document数
// in the first OP_REPLY batch
document query; // 查询对象,该对象同时包括一个或多个元素(orderby, explain, $snapshot),这些元素用于匹配(match)包含在结果集(result set)中的对象
[ document returnFieldSelector; ] // 可选顶. 要求返回的document应包含的字段(fields)
}
header | 消息标头。 |
flags | 位向量,用于指定操作的标志。位值对应于以下内容:
|
fullCollectionName | 完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称. 的串联,使用一个用于连接。例如,对于数据库foo 和集合bar ,完整集合名称为foo.bar 。 |
numberToSkip | 设置要忽略的文档数 - 从结果数据集中的第一个文档开始 - 返回查询结果时。 |
numberToReturn | 将第一个OP_REPLY消息中的文档数限制为查询。但是,cursorID 如果结果多于,则数据库仍将建立游标并返回到客户端numberToReturn 。如果客户端驱动程序提供“限制”功能(如SQL LIMIT关键字),则由客户端驱动程序确保将不超过指定数量的文档返回给调用应用程序。如果numberToReturn 是0 ,db将使用默认的返回大小。如果数字为负数,则数据库将返回该数字并关闭光标。无法获取该查询的其他结果。如果numberToReturn 是 1 ,服务器将其视为-1 (自动关闭光标)。 |
query | 代表查询的BSON文档。该查询将包含一个或多个元素,所有元素必须匹配要包含在结果集中的文档。可能的因素包括 $query ,$orderby ,$hint ,和$explain 。 |
returnFieldsSelector | 可选的。限制返回文档中字段的BSON文档。所述 |
数据库将使用OP_REPLY消息响应 OP_REPLY消息。
2、4 OP_GET_MORE
OP_GET_MORE消息用于在数据库中查询集合中的文档。
struct {
MsgHeader header; // 标准消息类信息
int32 ZERO; // 0 - 为将来使用而保留的数据位
cstring fullCollectionName; // 完整的集合名称,形如:"dbname.collectionname"
int32 numberToReturn; // 返回的document数
int64 cursorID; // 在REPLY消息中的Cursor标识符,其必须来自于数据库
}
领域 | 描述 |
---|---|
header | 消息标头。 |
ZERO | 整数值0.保留供将来使用。 |
fullCollectionName | 完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称. 的串联,使用一个用于连接。例如,对于数据库foo 和集合bar ,完整集合名称为foo.bar 。 |
numberToReturn | 将第一个OP_REPLY消息中的文档数限制为查询。但是,cursorID 如果结果多于,则数据库仍将建立游标并返回到客户端numberToReturn 。如果客户端驱动程序提供“限制”功能(如SQL LIMIT关键字),则由客户端驱动程序确保将不超过指定数量的文档返回给调用应用程序。如果numberToReturn 是0 ,db将使用默认的返回大小。 |
cursorID | OP_REPLY中的游标标识符。这必须是来自数据库的值。 |
数据库将使用OP_REPLY消息响应 OP_GET_MORE消息。
2、5 OP_DELETE
OP_DELETE消息用于从集合中删除一个或多个文档。
struct {
MsgHeader header; // 标准消息头信息
int32 ZERO; // 0 - 为将来使用而保留的数据位
cstring fullCollectionName; // 完整的集合名称,形如:"dbname.collectionname"
int32 flags; // 位向量,参见下面介绍
document selector; // 查询条件
}
header | 消息标头。 |
ZERO | 整数值0.保留供将来使用。 |
fullCollectionName | 完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称. 的串联,使用一个用于连接。例如,对于数据库foo 和集合bar ,完整集合名称为foo.bar 。 |
flags | 位向量,用于指定操作的标志。位值对应于以下内容:
|
selector | BSON文档,表示用于选择要删除的文档的查询。选择器将包含一个或多个元素,所有元素必须匹配要从集合中删除的文档。 |
OP_DELETE消息没有响应。
2、6 OP_KILL_CURSORS
OP_KILL_CURSORS消息用于关闭数据库中的活动游标。这是确保在查询结束时回收数据库资源所必需的。
struct {
MsgHeader header; // 标准消息头信息
int32 ZERO; // 0 - 为将来使用而保留的数据位
int32 numberOfCursorIDs; // 消息中游标数
int64* cursorIDs; // cursorIDs的关闭顺序
}
header | 消息标头。 |
ZERO | 整数值0.保留供将来使用。 |
numberOfCursorIDs | 消息中的游标ID数。 |
cursorIDs | 要关闭的游标ID的“数组”。如果有多个,则依次将它们按顺序写入套接字。 |
如果读取游标直到耗尽(读取直到OP_QUERY或OP_GET_MORE为游标id返回零),则无需终止游标。
2、7 OP_COMMAND
OP_COMMAND
是集群内部的,不应由客户端或驱动程序实现。OP_COMMAND
是内部用于一个MongoDB服务器向另一个MongoDB服务器发出的集群内数据库命令请求的有线协议消息。
struct {
MsgHeader header; // standard message header
cstring database; // the name of the database to run the command on
cstring commandName; // the name of the command
document metadata; // a BSON document containing any metadata
document commandArgs; // a BSON document containing the command arguments
inputDocs; // a set of zero or more documents
}
header | 标准消息头。 |
database | 要运行命令的数据库的名称。 |
commandName | 命令的名称。有关数据库命令的列表,请参阅数据库命令。 |
metadata | 可供系统将任何元数据附加到内部命令,这些内部命令不是客户端驱动程序提供的命令参数的一部分 |
commandArgs | 包含命令参数的BSON文档。 有关 |
inputDocs | 零个或多个文档充当命令的输入。对于需要从客户端发送大量数据的命令很有用,例如批量插入。 |
2、8 OP_MSG
OP_MSG
是一种可扩展的消息格式,旨在包含其他操作码的功能。在3.6版本中新增。
OP_MSG {
MsgHeader header; // standard message header
uint32 flagBits; // message flags
Sections[] sections; // data sections
optional<uint32> checksum; // optional CRC-32C checksum
}
header | 标准消息头。 |
flagBits | 包含消息标志的整数位掩码。 |
sections | 消息体部分。 |
checksum | 可选CRC-32C校验和 |
3、数据库响应消息
3、1 OP_REPLY
该OP_REPLY
消息由数据库响应于发送 OP_QUERY或OP_GET_MORE消息。
struct {
MsgHeader header; // standard message header
int32 responseFlags; // bit vector - see details below
int64 cursorID; // cursor id if client needs to do get more's
int32 startingFrom; // where in the cursor this reply is starting
int32 numberReturned; // number of documents in the reply
document* documents; // documents
}
header | 消息标头。 |
responseFlags | 用于指定标志的位向量。位值对应于以下内容:
|
cursorID | 在cursorID 这OP_REPLY是的一部分。如果查询的结果集适合一个OP_REPLY消息,则为cursorID 0.这cursorID 必须在用于获取更多数据的任何 OP_GET_MORE消息中使用,并且必须在不再需要时通过OP_KILL_CURSORS消息由客户端关闭。 |
startingFrom | 光标中的起始位置。 |
numberReturned | 回复中的文件数量。 |
documents | 退回的文件。 |
3、2 OP_COMMANDREPLY
这OP_COMMANDREPLY
是一个内部使用的有线协议消息,用于回复一个MongoDB服务器向另一个MongoDB服务器发出的集群内OP_COMMAND请求。
struct {
MsgHeader header; // A standard wire protocol header
document metadata; // A BSON document containing any required metadata
document commandReply; // A BSON document containing the command reply
document outputDocs; // A variable number of BSON documents
}
header | 标准消息头。 |
metadata | 可供系统将任何元数据附加到内部命令,这些内部命令不是客户端驱动程序提供的命令参数的一部分。 |
commandReply | 包含命令回复的BSON文档。 |
outputDocs | 对于可以返回大量数据的命令很有用,例如查找或聚合。 该字段目前尚未使用。 |
参考:https://blog.youkuaiyun.com/hengyunabc/article/details/6897540
https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply