MongoDB的网络协议——MongoDB Wire Protocol

一、简介

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旨在具有以下三个特征:

  1. 轻量级

    将空间开销保持在最低限度对于任何数据表示格式都很重要,尤其是在通过网络使用时。

  2. 穿越

    BSON旨在轻松遍历。这是其作为主要的数据表示角色的重要属性的MongoDB。

  3. 高效

    由于使用C数据类型,在大多数语言中可以非常快速地将数据编码到BSON并从BSON解码。

BSON主要类型和格式:

e_name::=cstringcstring 类型的 元素名 
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 int64UTC日期时间
 |"\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 stringJavaScript 代码
 |"\x0E" e_name string放弃使用
 |"\x0F" e_name code_w_sJavaScript code w/ scope
 |"\x10" e_name int3232位整数
 |"\x11" e_name uint64时间戳
 |"\x12" e_name int6464位整数
 |"\x13" e_name decimal128128位十进制浮点数
 |"\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消息。客户端可以使用requestIDresponseTo字段将查询响应与原始查询相关联。
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

位向量,用于指定操作的标志。位值对应于以下内容:

  • 0对应Upsert。如果设置,如果找不到匹配的文档,数据库将把提供的对象插入到集合中。
  • 1对应于MultiUpdate。如果设置,数据库将更新集合中的所有匹配对象。否则只更新第一个匹配的文档。
  • 231保留。必须设置为0。
selectorBSON文档,指定用于选择要更新的文档的查询。
updateBSON文档,指定要执行的更新。

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

位向量,用于指定操作的标志。位值对应于以下内容:

  • 0对应于ContinueOnError。如果设置,则数据库将不会停止处理批量插入(如果一个失败)(例如,由于ID重复)。这使得批量插入的行为类似于一系列单个插入,除非在任何插入失败时设置lastError,而不仅仅是最后一个插入失败。如果发生多个错误,则getLastError仅报告最新错误。(1.9.1中的新内容)
  • 131保留。必须设置为0。
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

位向量,用于指定操作的标志。位值对应于以下内容:

  • 0被预定了。必须设置为0。
  • 1对应于TailableCursor。Tailable意味着在检索最后一个数据时不关闭游标。相反,光标标记最终对象的位置。如果收到更多数据,您可以稍后从它所在的位置继续使用光标。与任何“潜在游标”一样,游标可能在某些时候变为无效(CursorNotFound) - 例如,如果它引用的最终对象被删除。
  • 2对应于SlaveOk.Allow查询副本奴隶。通常这些返回错误,除了名称空间“local”。
  • 3对应OplogReplay。仅限内部复制使用 - 不应设置驱动程序。
  • 4对应于NoCursorTimeout。服务器通常在不活动时间(10分钟)后超时空闲游标,以防止过多的内存使用。设置此选项以防止这种情况。
  • 5对应于AwaitData。与TailableCursor一起使用。如果我们在数据的末尾,阻塞一段时间而不是返回没有数据。超时后,我们会照常恢复。
  • 6对应于排气。假设客户端将完全读取所有查询的数据,在多个“更多”包中流式传输数据。当你提取大量数据并且知道你想把它全部拉下来时更快。注意:除非关闭连接,否则不允许客户端读取所有数据。
  • 7对应于偏见。如果某些分片出现故障,则从mongos获取部分结果(而不是抛出错误)
  • 831保留。必须设置为0。
fullCollectionName完整的集合名称; 即名称空间。完整集合名称是数据库名称与集合名称.的串联,使用一个用于连接。例如,对于数据库foo和集合bar,完整集合名称为foo.bar
numberToSkip设置要忽略的文档数 - 从结果数据集中的第一个文档开始 - 返回查询结果时。
numberToReturn将第一个OP_REPLY消息中的文档数限制为查询。但是,cursorID如果结果多于,则数据库仍将建立游标并返回到客户端numberToReturn。如果客户端驱动程序提供“限制”功能(如SQL LIMIT关键字),则由客户端驱动程序确保将不超过指定数量的文档返回给调用应用程序。如果numberToReturn0,db将使用默认的返回大小。如果数字为负数,则数据库将返回该数字并关闭光标。无法获取该查询的其他结果。如果numberToReturn是 1,服务器将其视为-1(自动关闭光标)。
query代表查询的BSON文档。该查询将包含一个或多个元素,所有元素必须匹配要包含在结果集中的文档。可能的因素包括 $query$orderby$hint,和$explain
returnFieldsSelector

可选的。限制返回文档中字段的BSON文档。所述returnFieldsSelector含有一种或多种元素,其中的每一个是应返回字段的名称,以及整数值1。在JSON表示法中,areturnFieldsSelector限制字段ab并且 c将是:{ a : 1, b : 1, c : 1}

数据库将使用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关键字),则由客户端驱动程序确保将不超过指定数量的文档返回给调用应用程序。如果numberToReturn0,db将使用默认的返回大小。
cursorIDOP_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

位向量,用于指定操作的标志。位值对应于以下内容:

  • 0对应于SingleRemove。如果设置,则数据库将仅删除集合中的第一个匹配文档。否则将删除所有匹配的文档。
  • 131保留。必须设置为0。
selectorBSON文档,表示用于选择要删除的文档的查询。选择器将包含一个或多个元素,所有元素必须匹配要从集合中删除的文档。

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_QUERYOP_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文档。

有关commandName其参数的信息,请参阅指定的文档

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

用于指定标志的位向量。位值对应于以下内容:

  • 0对应于CursorNotFound。getMore调用时设置但光标id在服务器上无效。返回零结果。
  • 1对应于QueryFailure。查询失败时设置。结果包含一个文档,其中包含描述失败的“$ err”字段。
  • 2对应ShardConfigStale。应该忽略这一点。只会mongos看到这个集合,在这种情况下,它需要从服务器更新配置。
  • 3对应于AwaitCapable。在服务器支持AwaitData Query选项时设置。如果没有,客户端应该在Tailable游标的getMore之间稍微睡一会儿。Mongod版本1.6支持AwaitData,因此始终设置AwaitCapable。
  • 431保留。忽视。
cursorIDcursorID这OP_REPLY是的一部分。如果查询的结果集适合一个OP_REPLY消息,则为cursorID0.这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.csdn.net/hengyunabc/article/details/6897540

            http://bsonspec.org/spec.html

            https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值