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对应到相应的上下文。

相关文章
相关标签/搜索