想象一下以下案例:
会发生什么:
Meteor.Collection
客户端上的 s 将被更新,即插入转发给所有客户端(这意味着一个插入消息发送给 1,000 个客户端)服务器确定需要更新哪个客户端的 CPU 成本是多少?
只有插入的值才会转发给客户端,而不是整个列表,这是否准确?
这在现实生活中如何运作?是否有任何可用的此类规模的基准或实验?
最佳答案
简短的回答是只有新数据才能通过网络发送。这是
它是如何工作的。
Meteor 服务器的三个重要部分负责管理
订阅:发布函数,它定义了什么的逻辑
订阅提供的数据; Mongo 驱动程序,它监视
更改数据库;和合并框,它结合了所有
客户端的事件订阅并通过网络将它们发送到
客户。
发布功能
每次 Meteor 客户端订阅一个集合时,服务器都会运行一个
发布功能。发布函数的工作是找出集合
其客户应拥有的文档并发送每个文档属性
进入合并框。它为每个新订阅客户端运行一次。你
可以在发布函数中放入任何你想要的 JavaScript,比如
使用 this.userId
进行任意复杂的访问控制.发布
函数通过调用 this.added
将数据发送到合并框, this.changed
和this.removed
.见
full publish documentation对于
更多细节。
大多数发布功能不必处理低级added
, changed
和 removed
API,不过。如果发布函数返回一个 Mongo
cursor, meteor 服务器自动连接Mongo的输出
驱动程序( insert
、 update
和 removed
回调)到
合并框( this.added
、 this.changed
和 this.removed
)。它很整洁
您可以在发布功能中预先进行所有权限检查,并且
然后直接将数据库驱动连接到合并框,无需任何用户
代码的方式。当自动发布打开时,即使是这一点
hidden:服务器自动为每个文档中的所有文档设置一个查询
集合并将它们推送到合并框中。
另一方面,您不仅限于发布数据库查询。
例如,您可以编写一个读取 GPS 位置的发布函数
来自 Meteor.setInterval
内的设备,或轮询旧的 REST API
来自另一个网络服务。在这些情况下,您将更改
通过调用低级合并框added
, changed
和 removed
DDP API。
蒙戈司机
Mongo 驱动程序的工作是观察 Mongo 数据库中的更改
实时查询。这些查询持续运行并返回更新作为
调用 added
更改结果, removed
, 和 changed
回调。
Mongo 不是实时数据库。所以司机投票。它保持一个
每个事件实时查询的最后一个查询结果的内存副本。开
每个轮询周期,它将新结果与之前保存的结果进行比较
结果,计算最小集合added
, removed
, 和 changed
描述差异的事件。如果多个调用者注册
同一个实时查询的回调,驱动程序只观看一份
查询,使用相同的结果调用每个注册的回调。
每次服务器更新一个集合时,驱动程序都会重新计算每个
对该集合的实时查询(Meteor 的 future 版本将公开一个
用于限制哪些实时查询在更新时重新计算的扩展 API。)
驱动程序还会在 10 秒的计时器上轮询每个实时查询以捕获
绕过 Meteor 服务器的带外数据库更新。
合并框
合并框的工作是合并结果( added
、 changed
和 removed
调用)所有客户端的事件发布功能到单个数据中
流。每个连接的客户端都有一个合并框。它拥有一个
客户端 minimongo 缓存的完整副本。
在您只有一个订阅的示例中,合并框是
本质上是一种传递。但是一个更复杂的应用程序可以有多个
可能重叠的订阅。如果两个订阅都设置了
同一文档上的相同属性,合并框决定哪个值
优先并且只将它发送给客户端。我们没有曝光
用于设置订阅优先级的 API。目前,优先事项是
由客户端订阅数据集的顺序决定。第一个
客户订阅的优先级最高,第二个
订阅是次高的,依此类推。
因为合并框保存了客户端的状态,所以它可以发送最少的
保持每个客户端最新的数据量,无论发布什么
功能喂养它。
更新时会发生什么
所以现在我们已经为您的场景做好了准备。
我们有 1,000 个连接的客户端。每个人都订阅了同一个直播
Mongo 查询 ( Somestuff.find({})
)。由于每个客户端的查询都相同,因此驱动程序是
只运行一个实时查询。有 1,000 个事件合并框。并且
每个客户端的发布函数都注册了一个 added
, changed
,和removed
在进入合并框之一的实时查询上。
没有其他任何东西连接到合并框。
首先是 Mongo 驱动程序。当其中一个客户端插入新文档时
进入 Somestuff
,它会触发重新计算。 Mongo 驱动程序重新运行
查询 Somestuff
中的所有文档,将结果与
内存中的先前结果,发现有一个新文档,并且
调用 1,000 个已注册的每一个 insert
回调。
接下来,发布功能。这里几乎没有发生:每个
的 1,000 insert
回调通过以下方式将数据插入合并框
调用 added
.
最后,每个合并框对照其检查这些新属性
其客户端缓存的内存副本。在每种情况下,它发现
值尚未在客户端上,并且不影响现有值。所以
合并框发出 DDP DATA
SockJS 连接到其的消息
客户端并更新其服务器端内存副本。
总 CPU 成本是 diff 一个 Mongo 查询的成本,加上
1,000 个合并框检查其客户的状态并构建一个新的
DDP 消息负载。唯一通过线路流动的数据是单个
JSON 对象发送到 1,000 个客户端中的每一个,对应于新的
数据库中的文档,加上一条从服务器发送到服务器的 RPC 消息
进行原始插入的客户。
优化
这是我们绝对计划好的。
optimized the driver
在 0.5.1 中,每个不同的查询只运行一个观察者。
可以进行一些自动化改进,但最好的方法是 API
这让开发人员可以指定哪些查询需要重新运行。对于
例如,对于开发人员来说,将消息插入到
一个聊天室不应使对消息的实时查询无效
第二个房间。
在同一进程中,甚至在同一台机器上。一些应用
运行复杂的实时查询并且需要更多 CPU 来观察数据库。
其他人只有几个不同的查询(想象一个博客引擎),但是
可能有很多连接的客户端——这些需要更多的 CPU 来合并
盒子。分离这些组件将让我们缩放每个部分
独立。
提供旧行和新行。有了这个特性,一个数据库驱动程序
可以注册触发器而不是轮询更改。
关于benchmarking - Meteor 在众多客户之间共享大量收藏时的效率如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13113256/