100

想象以下情况:

  • 1,000 个客户端连接到显示“Somestuff”集合内容的 Meteor 页面。

  • “Somestuff”是一个包含 1,000 个项目的集合。

  • 有人在“Somestuff”集合中插入了一个新项目

会发生什么:

  • 客户端上的所有Meteor.Collections 都将被更新,即插入转发给所有客户端(这意味着一条插入消息发送给 1,000 个客户端)

服务器确定需要更新哪个客户端的 CPU 成本是多少?

仅将插入的值转发给客户端而不是整个列表是否准确?

这在现实生活中如何运作?有没有这种规模的基准或实验可用?

4

4 回答 4

119

简短的回答是只有新数据才能通过网络发送。这是它的工作原理。

Meteor 服务器管理订阅的三个重要部分:发布函数,它定义订阅提供哪些数据的逻辑;Mongo 驱动程序,它监视数据库的变化;和合并框,它结合了客户端的所有活动订阅,并通过网络将它们发送给客户端。

发布功能

每次 Meteor 客户端订阅一个集合时,服务器都会运行一个 发布函数。发布功能的工作是找出其客户端应该拥有的文档集,并将每个文档属性发送到合并框。它为每个新的订阅客户端运行一次。你可以在发布函数中放入任何你想要的 JavaScript,比如使用this.userId. this.added发布函数通过调用和将数据发送this.changed到 合并框中this.removed。有关更多详细信息,请参阅 完整的发布文档

不过,大多数发布函数不必乱用低级 added,changedremovedAPI。如果发布函数返回一个 Mongo 游标,Meteor 服务器会自动将 Mongo 驱动程序的输出(insertupdateremoved回调)连接到合并框的输入(this.addedthis.changedthis.removed)。您可以在发布函数中预先进行所有权限检查,然后直接将数据库驱动程序连接到合并框,而无需任何用户代码,这非常简洁。并且当自动发布打开时,即使是这一点也被隐藏了:服务器会自动为每个集合中的所有文档设置一个查询,并将它们推送到合并框中。

另一方面,您不仅限于发布数据库查询。例如,您可以编写一个发布函数,该函数从 中的设备读取 GPS 位置Meteor.setInterval,或从另一个 Web 服务轮询旧版 REST API。在这些情况下,您将通过调用低级和 DDP API 来对合并框进行added更改。changedremoved

蒙哥司机

Mongo 驱动程序的工作是监视 Mongo 数据库中实时查询的更改。这些查询连续运行,并随着结果的变化通过调用addedremovedchanged回调返回更新。

Mongo 不是实时数据库。所以司机投票。它为每个活动的实时查询保留最后一个查询结果的内存副本。在每个轮询周期中,它将新结果与之前保存的结果进行比较,计算描述差异的最小集合addedremovedchanged 事件。如果多个调用者为同一个实时查询注册回调,驱动程序只观察查询的一个副本,调用每个注册的回调并返回相同的结果。

每次服务器更新集合时,驱动程序都会重新计算该集合上的每个实时查询(Meteor 的未来版本将公开一个缩放 API,用于限制哪些实时查询在更新时重新计算。)驱动程序还以 10 秒的计时器轮询每个实时查询以捕获绕过 Meteor 服务器的带外数据库更新。

合并框

合并框的工作是将客户端所有活动发布函数的结果(addedchanged调用removed )合并到单个数据流中。每个连接的客户端都有一个合并框。它拥有客户端 minimongo 缓存的完整副本。

在您只有一个订阅的示例中,合并框本质上是一个传递。但更复杂的应用程序可能有多个可能重叠的订阅。如果两个订阅都在同一个文档上设置了相同的属性,则合并框会决定哪个值具有优先权并仅将其发送给客户端。我们还没有公开设置订阅优先级的 API。目前,优先级由客户端订阅数据集的顺序决定。客户端进行的第一个订阅具有最高优先级,第二个订阅次之,依此类推。

因为合并框保存客户端的状态,所以它可以发送最少的数据量以使每个客户端保持最新状态,无论发布函数为其提供什么。

更新会发生什么

所以现在我们已经为你的场景做好了准备。

我们有 1,000 个连接的客户。每个都订阅了相同的实时 Mongo 查询 ( Somestuff.find({}))。由于每个客户端的查询都是相同的,因此驱动程序只运行一个实时查询。有 1,000 个活动的合并框。并且每个客户端的发布功能都在该实时查询上注册了一个added, changed, 和 removed,该实时查询会馈送到一个合并框。没有其他东西连接到合并框。

首先是 Mongo 驱动程序。当其中一个客户端将新文档插入Somestuff时,它会触发重新计算。Mongo 驱动程序对 中的所有文档重新运行查询Somestuff,将结果与内存中的先前结果进行比较,发现有一个新文档,并调用 1,000 个注册的insert回调中的每一个。

接下来是发布功能。这里几乎没有发生什么:1000 个insert回调中的每一个都通过调用added.

最后,每个合并框都会根据其客户端缓存的内存副本检查这些新属性。在每种情况下,它都会发现这些值尚未在客户端上,并且不会影响现有值。因此,合并框在 SockJS 与其客户端的连接上发出 DDPDATA消息,并更新其服务器端内存中的副本。

总 CPU 成本是 diff 一个 Mongo 查询的成本,加上 1,000 个合并框检查其客户端状态并构建新的 DDP 消息有效负载的成本。通过网络传输的唯一数据是发送到 1,000 个客户端中的每一个的单个 JSON 对象,对应于数据库中的新文档,以及从进行原始插入的客户端到服务器的一条 RPC 消息。

优化

这是我们绝对计划好的。

  • 更高效的 Mongo 驱动程序。我们 在 0.5.1 中优化了驱动程序 ,使每个不同的查询只运行一个观察者。

  • 并非每个数据库更改都应该触发重新计算查询。我们可以进行一些自动化改进,但最好的方法是使用 API,让开发人员指定哪些查询需要重新运行。例如,开发人员很明显,将消息插入一个聊天室不应使对第二个聊天室中消息的实时查询无效。

  • Mongo驱动、发布函数和合并框不需要运行在同一个进程中,甚至不需要运行在同一台机器上。一些应用程序运行复杂的实时查询并且需要更多 CPU 来监视数据库。其他的只有几个不同的查询(想象一个博客引擎),但可能有很多连接的客户端——这些需要更多的 CPU 用于合并框。分离这些组件将使我们能够独立地缩放每个部分。

  • 许多数据库支持在更新行时触发的触发器并提供旧行和新行。使用该功能,数据库驱动程序可以注册触发器而不是轮询更改。

于 2012-12-13T19:37:01.560 回答
29

根据我的经验,从 0.7.0.1 版本开始,在 Meteor 中共享大量集合的同时使用许多客户端基本上是不可行的。我将尝试解释原因。

如上述帖子以及https://github.com/meteor/meteor/issues/1821中所述,meteor 服务器必须在合并框中为每个客户端保留一份已发布数据的副本。这就是 Meteor 魔法发生的原因,但也导致任何大型共享数据库被重复保存在节点进程的内存中。即使对静态集合使用可能的优化,例如(Is there a way to tell meteor a collection is static (will never change)?),我们在节点进程的 CPU 和内存使用方面遇到了一个巨大的问题。

在我们的案例中,我们向每个客户端发布了 15k 个完全静态的文档集合。问题是,在连接时将这些文档复制到客户端的合并框(在内存中)基本上会使 Node 进程在几乎一秒钟内达到 100% CPU,并导致大量额外的内存使用。这本质上是不可扩展的,因为任何连接的客户端都会使服务器瘫痪(同时连接会相互阻塞),并且内存使用量会随着客户端的数量线性增加。在我们的例子中,每个客户端导致额外的大约 60MB内存使用,即使传输的原始数据只有大约 5MB。

在我们的例子中,因为集合是静态的,我们通过将所有文档作为.json文件发送来解决这个问题,该文件由 nginx gzip 压缩,然后将它们加载到匿名集合中,导致只有约 1MB 的数据传输而无需额外的 CPU或节点进程中的内存和更快的加载时间。对这个集合的所有操作都是通过使用_id服务器上小得多的出版物中的 s 来完成的,从而保留了 Meteor 的大部分好处。这使该应用程序可以扩展到更多客户。此外,由于我们的应用程序大部分是只读的,我们通过在 nginx 后面运行多个 Meteor 实例并进行负载平衡(尽管使用单个 Mongo)进一步提高了可伸缩性,因为每个 Node 实例都是单线程的。

然而,在多个客户端之间共享大型可写集合的问题是一个需要 Meteor 解决的工程问题。可能有比为每个客户端保留所有内容的副本更好的方法,但这需要认真考虑作为分布式系统问题。当前的大量 CPU 和内存使用问题无法扩展。

于 2014-02-17T17:28:32.087 回答
4

您可以用来回答这个问题的实验:

  1. 安装测试流星:meteor create --example todos
  2. 在 Webkit 检查器 (WKI) 下运行它。
  3. 检查通过网络传输的XHR消息的内容。
  4. 观察到整个集合没有跨线移动。

有关如何使用 WKI 的提示,请查看这篇文章。它有点过时了,但大部分仍然有效,尤其是对于这个问题。

于 2012-11-18T02:59:23.603 回答
3

这仍然是一岁,因此我认为“流星1.0”之前的知识,所以事情可能再次改变?我还在调查这个。 http://meteorhacks.com/does-meteor-scale.html 导致“如何缩放 Meteor?” 文章 http://meteorhacks.com/how-to-scale-meteor.html

于 2014-12-05T00:10:23.583 回答