DDP
DDP定义
DDP是一个客户端和服务端之间的协议,他支持两种操作:
- 由
客户端向服务端发起远程过程调用客户端订阅数据,在他们变化时,服务器仍然保持向客户端发起通知。
本文定义了版本为"pre1"的DDP,以上仅仅是粗略的描述而非完整明确的定义。
一般消息结构
无论SockJS还是WebSockets,DDP都将使用较低级别的消息传输方式。(现在,你可以通过URL连接SockJS的/sockjs以及WebSockets的/websocket。后者很可能将改变为主应用URL指定的WebSocket子协议)
DDP消息就是指定了EJSON类型字段的JSON对象。每个消息都有一个msg字段来指定消息类型,或根据其他字段确定消息类型。
建立DDP连接
message分类
connect(client → server)
session: string (尝试连接到现有DDP会话)version: string (拟定的协议版本)support: [string] (客户端支持的协议版本,按优先顺序排列)
connected(server → client)
session: string (DDP会话标识)
failed(server → client)
version: string (建议连接协议版本)步骤
服务器可能会发送一个缺失键名为msg的初始化message,如果是这样的,客户端会忽略它,也不会等待此message。(此message是通过SockJS传输帮助实现代码热部署的,可能不应该,但目前我们是通过WebSocket实现的)
客户端发送connect消息。- 如果
服务器允许通信的消息的version与接收到的connect消息匹配,服务器会发送connected消息。- 否则
服务器会关闭底层传输,发送failed消息,告之允许通信的DDP版本以及connect消息中的support字段。客户端建新connect消息尝试使用不同版本的DDP连接。客户端会发送众多的connect消息,直到连接匹配。如果服务器还不支持,就会忽略掉这些附加信息。
connect消息的support字段存储的版本信息是根据客户端的喜好优先级排列的。如果按照这个顺序,由客户端提出的version不是服务器支持的,服务器必会发送failed消息,强制客户端切换到更好的版本。
当客户端第一次连接到服务器,他会设置version为自己最认可的版本。如果有需要,客户端可以记住最终能与服务端匹配的版本。如果服务端能拥有更好的版本或者客户端已经升级,客户端始终依靠服务端发来的failed消息。
管理数据
message分类
sub(client → server)
id: string (订阅的标识符)name: string (订阅的名字)params: [EJSON] (可选的订阅参数)
unsub(client → server)
id: string (sub消息中的id)
nosub(server → client)
id: string (sub消息中的id)
error: Error(可选的错误,订阅错误总结或者订阅不存在等)added(server → client)
collection: string (Collecction名字)id: string (Documentid)fields: EJSON (可选的EJSON值)
changed(server → client)
collection: string (Collecction名字)id: string (Documentid)fields: EJSON (可选的EJSON值)cleared: [string] (可选的,要删除的字段名)
removed(server → client)
collection: string (Collecction名字)id: string (Documentid)
ready(server → client)
subs: [string] (所有通过sub传递的id集)
addedBefore(server → client)
collection: string (Collecction名字)id: string (Documentid)fields: EJSON (可选的EJSON值)before: string/null (Document添加前的id,或者null表示添加完毕)
movedBefore(server → client)
collection: string (Collecction名字)id: string (Documentid)before: string/null (Document删除前的id,或者null表示删除完毕)
步骤
客户端指定感兴趣的信息集,发送sub消息给服务器。- 在任何时间里,
sub消息都会被通知到,服务器再发送数据到客户端。数据包含了added、changed、removed消息。这些消息的本地数据模型将会被跟踪。
added消息表示了本地数据集的一个Document。id字段将被指定为Document的id,其他字段将被指定为Document的其他字段。minimongo通过特殊的方式将id转化为_id以存储到Mongo的文档集中。changed消息表示了本地数据集的一个Document有了新的字段值或某些字段被删除了。id字段指定了被改变了的Document的id,fields如果存在,表示文档中哪些字段值应该被替换。需要清除的字段应该放在cleared中以数组的形式呈现。removed消息表示了本地数据集的一个Document需要删除了。id字段指定了需要删除的Document的id。- 如果一个
Collection被订购过,addedBefore消息可以取代added消息。包含有该id的Document在被添加后可插入before字段。如果before字段设置为null,Document将被添加,直到结束。对于一个给定的集合,服务器只应发送added或addedBefore消息,而不是两者混合起来发。并且只能发送movedBefore消息给使用addedBefore消息的Collection。注意 : 被订购过的
Collection的DDP消息没有使用在Meteor中,不过今后将会使用。
客户端保持每一个Collection的数据。每个订阅不会做自己的数据集,但重复订阅会导致服务器发送关于Collection数据的字段联盟。例如,订阅器A说文档x有字段{foo:1,bar:2},订阅器B说文档x有{foo:1,baz:3},此时客户端将通知文档x拥有字段{foo:1,bar:2,baz:3}。如果字段值因为不同订阅器发生冲突,服务器会发送一个可用的字段值。- 当一个或多个订阅完成首批数据的发送,服务器将发送
ready消息并伴随他们的id信息。
远程过程调用
message分类
method(client → server)
method: string (方法名)params: [EJSON] (可选的方法参数)id: string (方法调用的客户端标识)
result(server → client)
id: string (method消息中的id)error: Error (可选的,方法调用时的错误或者方法不存在)result: EJSON (可选的,方法的返回值)
updated(server → client)
methods: string (所有调用过method消息的id)
步骤
客户端发送一个method消息到服务器服务端响应一个result消息到客户端,带有方法调用的返回值或者错误信息。- 如果
客户端接受过订阅,方法调用将直接影响数据。一旦服务端基于此方式完成所有相应数据的发送,服务器会发送一个updated消息给客户端,并带有所有方法的id集。
错误
result和nosub消息中会出现一个可选的error字段,一个错误消息具备如下字段:
- error : number (错误号)
- reason : string (可选的错误原因)
- details : string (可选的错误详情)
error用来呈现订阅或者远程调用方法时出现的错误信息,订阅不存在或者调用方法不存在时,也通过这个传递信息。
客户端也能发送其他的错误消息给服务端,这样的消息将以error为最顶级直接呈现,并包含以下信息:
- 不合法的JSON对象信息
- 未知的
msg类型 - 其他畸形客户端请求(不包括所需字段)
- 发送第一次
connect消息或者connect消息初始化无关的其他内容
这样的error消息将包含以下字段
- reason : string (描述错误的字符串)
- offendingMessage : ** (包含原来解析正确的消息)
使用
做了两个例子,可以参照这篇文章做一下。
附录
EJSON是一种嵌入扩展JSON的JSON对象。他支持所有平时所见的JSON类型,同时附加了如下内容:
Dates
{"$date": MILLISECONDS_SINCE_EPOCH}
Binary data:
{"$binary": BASE_64_STRING}
基于64位字符串,有符号+和/,没有长度限制。
转义内容,否则看上去更像是EJSON类型:
{"$escape": THING}
例如,把JSON值{$date:10000}装进EJSON对象中:
{"$escape": {"$date": 10000}}
注意:
转义内容的键必须存放在下级,你也可以再嵌套EJSON,例如,下面将$date映射到一个日期对象上:
{"$escape": {"$date": {"$date": 32491}}}
User-specified types
{"$type": TYPENAME, "$value": VALUE}
实现EJSON应尽量保证键位顺序。如果允许,也可以不必这样。
MongoDB依赖键位顺序。在MongoDB中使用EJSON是,实现的EJSON必须保持键位顺序。
有关EJSON的详细信息可以到Meteor的文档中心-EJSON查阅,或是查看讲解EJSON的视频。