前端的全栈混合之路Meteor篇:分布式数据协议DDP剖析

本文属于进阶篇,并不是太适合新人阅读,但纯粹的学习还是可以的,因为后续会实现很多个ddp的版本用于web端、nodejs端、安卓端和ios端,提前预习和复习下。ddp协议是一个C/S架构的协议,但是客户端也同时可以是服务端。

什么是DDP?

DDP (Distributed Data Protocol) 是Meteor框架中使用的一种简单而强大的发布/订阅协议。它允许客户端和服务器之间进行实时数据同步,是Meteor实现实时应用的核心技术之一。

DDP的主要特点

  1. 传输层灵活性: DDP可以基于WebSocket,也可以通过HTTP长轮询等方式实现,例如使用SockJS在不支持WebSocket的环境中工作。
  2. JSON格式: 所有的消息都使用JSON格式,便于解析和调试。-需要注意的是,实际的传输是用的EJSON进行序列化和反序列,从而支持了更多的对象传输,详参考 前端的全栈混合之路Meteor篇(四):支持自定义对象序列化的EJSON介绍
  3. 发布/订阅模型: 允许客户端订阅服务器端的数据集,并在数据变化时接收更新。它的应用例子见文章 前端的混合全栈之路Meteor篇(三):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
  4. 方法调用: 客户端可以调用服务器端的方法,实现远程过程调用(RPC)。基于它的应用例子见文章 前端的全栈混合之路Meteor篇(二):RPC方法注册及调用
  5. 实时更新: 当订阅的数据发生变化时,服务器会自动将更新推送给客户端。这部分是和发布订阅关联的,本文会涉及到底层实现
  6. 会话管理: 使用会话ID来维护客户端和服务器之间的连接状态。

DDP协议的详细过程

1. 握手和连接建立

  1. 客户端发送 connect 消息,可能包含版本信息和会话ID(如果是重连)。
  2. 服务器回复 connected 消息,包含新的会话ID。
  3. 如果是重连,服务器会恢复之前的订阅和方法调用状态。

2. 保活和心跳机制

  1. 客户端定期(通常每45秒)发送 ping 消息。
  2. 服务器回复 pong 消息。
  3. 如果超时未收到 pong,客户端可能会尝试重新连接。

3. 方法调用

  1. 客户端发送 method 消息,包含方法名和参数。
  2. 服务器执行方法,可能会触发数据变更。
  3. 服务器发送 result 消息,包含方法执行结果或错误信息。
  4. 如果方法导致数据变更,服务器会发送相应的 addedchangedremoved 消息。

4. 发布和订阅

  1. 客户端发送 sub 消息,包含订阅名称和参数。
  2. 服务器开始发送相关数据:
    • added 消息用于新增的文档
    • changed 消息用于更新的文档
    • removed 消息用于删除的文档
  3. 服务器发送 ready 消息,表示初始数据集已发送完毕。
  4. 之后,服务器会持续发送 addedchangedremoved 消息以保持数据同步。
  5. 客户端可以发送 unsub 消息来取消订阅。

DDP消息类型详解

  1. connect: 客户端发起连接请求

    {"msg": "connect", "version": "1", "support": ["1", "pre2", "pre1"]}
    
  2. connected: 服务器确认连接成功

    {"msg": "connected", "session": "RandomSessionId123"}
    
  3. ping/pong: 心跳消息

    {"msg": "ping", "id": "unique-id-123"}
    {"msg": "pong", "id": "unique-id-123"}
    
  4. sub/unsub: 订阅和取消订阅

    {"msg": "sub", "id": "random-id", "name": "publicationName", "params": []}
    {"msg": "unsub", "id": "random-id"}
    
  5. added/changed/removed: 数据更新通知

    {"msg": "added", "collection": "collectionName", "id": "documentId", "fields": {}}
    {"msg": "changed", "collection": "collectionName", "id": "documentId", "fields": {}}
    {"msg": "removed", "collection": "collectionName", "id": "documentId"}
    
  6. ready: 初始数据加载完成通知

    {"msg": "ready", "subs": ["subscription-id-1", "subscription-id-2"]}
    
  7. method/result: 方法调用和结果

    {"msg": "method", "method": "methodName", "params": [], "id": "call-id"}
    {"msg": "result", "id": "call-id", "result": {} }
    

DDP全生命周期时序图

可使用mermaid进行预览,时序图code如下

sequenceDiagram
    participant Client
    participant Server

    %% 握手和连接建立
    Client->>Server: connect {version: "1", support: ["1", "pre2", "pre1"]}
    Server-->>Client: connected {session: "RandomSessionId123"}

    %% 发布订阅
    Client->>Server: sub {id: "sub1", name: "posts", params: []}
    Server-->>Client: added {collection: "posts", id: "post1", fields: {...}}
    Server-->>Client: added {collection: "posts", id: "post2", fields: {...}}
    Server-->>Client: ready {subs: ["sub1"]}

    %% 实时更新
    loop Real-time updates
        Server-->>Client: changed {collection: "posts", id: "post1", fields: {...}}
        Server-->>Client: added {collection: "posts", id: "post3", fields: {...}}
        Server-->>Client: removed {collection: "posts", id: "post2"}
    end

    %% 方法调用
    Client->>Server: method {method: "addPost", params: [...], id: "m1"}
    Server-->>Client: added {collection: "posts", id: "post4", fields: {...}}
    Server-->>Client: result {id: "m1", result: {...}}

    %% 取消订阅
    Client->>Server: unsub {id: "sub1"}

    %% 心跳机制
    loop Keep-alive
        Client->>Server: ping {id: "ping1"}
        Server-->>Client: pong {id: "ping1"}
    end

时序图预览
ddp全生命周期时序图

DDP在Meteor中的应用

  1. 实时数据同步: 当服务器端的数据发生变化时,客户端可以立即收到更新。
  2. 用户界面响应: 通过DDP,用户界面可以实时反映数据的变化,提供流畅的用户体验。
  3. 分布式计算: 客户端可以调用服务器端的方法,实现复杂的计算或数据处理。
  4. 多客户端协作: 多个客户端可以同时订阅相同的数据集,实现实时协作功能。
  5. 离线支持: 通过本地缓存和重连机制,DDP可以支持离线操作和数据同步。

总结

DDP协议是Meteor框架的核心组件之一,它为实时Web应用提供了强大而灵活的数据同步机制。通过使用DDP,开发者可以轻松构建响应迅速、实时更新的现代Web应用,同时保持了在不同网络环境下的适应性。

上一篇:LeetCode1049:最后一块石头的重量