流星如何高效可以同时共享许多客户之间的巨大集合?(How efficient can Meteor

2019-06-18 16:34发布

想象一下下面的情况:

  • 1000个客户端连接到显示“Somestuff”收藏的内容流星页面。

  • “Somestuff”是一家集控股1000项。

  • 有人插入一个新的项目进入“Somestuff”集合

会发生什么:

  • 所有Meteor.Collection客户端上的旨意即转发到所有他们的插入被更新(这意味着发送到1000个客户端一个插入消息)

什么是要更新的CPU的服务器,以确定哪些客户需要的长期成本是多少?

它是准确的,只有插入的值将被转发到客户端,而不是整个列表?

如何在现实生活中这个工作呢? 是否有这样的规模的任何基准测试或试验是否可用?

Answer 1:

The short answer is that only new data gets sent down the wire. Here's how it works.

There are three important parts of the Meteor server that manage subscriptions: the publish function, which defines the logic for what data the subscription provides; the Mongo driver, which watches the database for changes; and the merge box, which combines all of a client's active subscriptions and sends them out over the network to the client.

Publish functions

Each time a Meteor client subscribes to a collection, the server runs a publish function. The publish function's job is to figure out the set of documents that its client should have and send each document property into the merge box. It runs once for each new subscribing client. You can put any JavaScript you want in the publish function, such as arbitrarily complex access control using this.userId. The publish function sends data into the merge box by calling this.added, this.changed and this.removed. See the full publish documentation for more details.

Most publish functions don't have to muck around with the low-level added, changed and removed API, though. If a publish function returns a Mongo cursor, the Meteor server automatically connects the output of the Mongo driver (insert, update, and removed callbacks) to the input of the merge box (this.added, this.changed and this.removed). It's pretty neat that you can do all the permission checks up front in a publish function and then directly connect the database driver to the merge box without any user code in the way. And when autopublish is turned on, even this little bit is hidden: the server automatically sets up a query for all documents in each collection and pushes them into the merge box.

On the other hand, you aren't limited to publishing database queries. For example, you can write a publish function that reads a GPS position from a device inside a Meteor.setInterval, or polls a legacy REST API from another web service. In those cases, you'd emit changes to the merge box by calling the low-level added, changed and removed DDP API.

The Mongo driver

The Mongo driver's job is to watch the Mongo database for changes to live queries. These queries run continuously and return updates as the results change by calling added, removed, and changed callbacks.

Mongo is not a real time database. So the driver polls. It keeps an in-memory copy of the last query result for each active live query. On each polling cycle, it compares the new result with the previous saved result, computing the minimum set of added, removed, and changed events that describe the difference. If multiple callers register callbacks for the same live query, the driver only watches one copy of the query, calling each registered callback with the same result.

Each time the server updates a collection, the driver recalculates each live query on that collection (Future versions of Meteor will expose a scaling API for limiting which live queries recalculate on update.) The driver also polls each live query on a 10 second timer to catch out-of-band database updates that bypassed the Meteor server.

The merge box

The job of the merge box is to combine the results (added, changed and removed calls) of all of a client's active publish functions into a single data stream. There is one merge box for each connected client. It holds a complete copy of the client's minimongo cache.

In your example with just a single subscription, the merge box is essentially a pass-through. But a more complex app can have multiple subscriptions which might overlap. If two subscriptions both set the same attribute on the same document, the merge box decides which value takes priority and only sends that to the client. We haven't exposed the API for setting subscription priority yet. For now, priority is determined by the order the client subscribes to data sets. The first subscription a client makes has the highest priority, the second subscription is next highest, and so on.

Because the merge box holds the client's state, it can send the minimum amount of data to keep each client up to date, no matter what a publish function feeds it.

What happens on an update

So now we've set the stage for your scenario.

我们有1000级连接的客户端。 每个订阅相同活蒙戈查询( Somestuff.find({}) 由于查询是针对每一个客户端一样,司机只运行一个实况查询。 有1000个活跃合并箱。 并注册的每个客户端的发布功能addedchangedremoved对送入的合并盒一个实况查询。 闲来无事连接到合并框。

首先,蒙戈驱动程序。 当一个客户端插入一个新的文档进Somestuff ,它触发重新计算。 蒙戈驱动程序重新运行在所有文档查询Somestuff ,并将结果与先前的结果在内存中,发现有一个新的文件,并调用每个注册的1000个的insert回调。

接下来,发布等功能。 有很少发生在这里:每个1000个的insert回调通过调用数据推到合并框added

最后,每个合并箱检查这些新属性针对其客户的高速缓存的内存副本。 在每一种情况下,它发现价值尚未在客户端上,并没有阴影的现有价值。 因此合并箱发出DDP DATA的SockJS连接到它的客户端上的消息,并更新其服务器端的内存拷贝。

CPU总成本差异比较一个蒙戈查询的费用,再加上1000的合并盒检查其客户的状态,并构建新的DDP消息有效载荷的成本。 即在导线中流动的唯一数据是发送给每个客户端1000的单个JSON对象,对应于新的文件在数据库中,加一个RPC消息该取得的原始插入客户端服务器

优化

下面是我们绝对有计划。

  • 更高效的蒙戈驱动程序。 我们优化了驱动程序在0.5.1运行仅每个不同的查询单站。

  • 不是每个DB变化应该触发查询的重新计算。 我们可以做一些自动化的改进,但最好的方法是一种API,允许开发人员指定的查询需要重新运行。 例如,这是显而易见的是,在第二个房间中插入一个消息到一个聊天室不应该为无效消息实况查询开发商。

  • 蒙戈驱动程序,发布功能,以及合并盒不需要在同一过程中,甚至在同一台机器上运行。 一些应用程序运行复杂的实时查询,需要更多的CPU来观看数据库。 其他人只有几个不同的查询(想象一个博客引擎),但可能许多连接的客户端 - 这些都需要做合并盒子更多的CPU。 分开这些组件将让我们独立地扩展每一块。

  • 许多数据库支持触发火灾时,该行更新,并提供新老行。 由于这一特点,数据库驱动程序可以注册一个触发器,而不是轮询更改。



Answer 2:

从我的经验,使用带有而在流星共享一个巨大的珍藏的许多客户基本上是不可行的,因为的0.7.0.1版本。 我会试着解释这是为什么。

如在上述描述后,也https://github.com/meteor/meteor/issues/1821 ,流星服务器必须保持公布的数据的副本,在合并框中的每个客户端。 这是允许的流星魔术发生,但也导致任何大型共享数据库被反复不停地在节点进程的内存。 使用静态的集合,如在一个可能的优化,即使( 有没有办法告诉流星集合是静态的(不会改变)? ),我们经历与节点工艺的CPU和内存使用一个巨大的问题。

在我们的例子中,我们发布的15K文件的集合,每个客户端,这是完全静态的。 问题是,复制这些文件到客户端的合并盒(在内存中)在连接基本上带来的节点处理100%的CPU几乎一秒钟,造成一个大的内存的使用率。 这本质上是不可扩展的,因为任何连接的客户端将带给服务器瘫痪(和同时连接将阻止对方)和内存使用情况将在客户端的数量呈线性上升。 在我们的例子中,每个客户端造成内存使用额外的60MB〜,即使传输的原始数据仅为5MB左右。

在我们的例子,因为集合是静态的,我们通过发送所有的文件作为解决这一问题.json文件,这是由nginx的gzip压缩,并装入一个匿名收集,导致只有一个数据的〜1MB转移,无在节点处理附加的CPU或存储器和更快的加载时间。 在这个集合中的所有操作均通过做_id从服务器上要小得多出版物S,允许保留大部分的流星的好处。 这使得应用程序扩展到更多的客户。 另外,由于我们的应用程序大部分是只读的,我们进一步运行多个实例流星背后负载平衡nginx的(虽然有一个蒙戈)提高了可扩展性,因为每个节点的实例是单线程的。

然而,多个客户端之间共享大量的,可写集合的问题是,需要由流星需要解决的工程问题。 有可能是比藏在心里的一个副本,为每个客户一个更好的办法,但需要认真考虑作为一个分布式系统的问题。 大量的CPU和内存使用情况的当前问题就不会扩大。



Answer 3:

实验,你可以用它来回答这个问题:

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

有关如何使用WKI看看这个技巧的文章 。 这是一个有点过时,但大多仍然有效,特别适用于这个问题。



Answer 4:

现在,这仍然是一岁,因此,我认为前“流星1.0”的知识,这样的事情可能又发生了变化? 我还在找这个。 http://meteorhacks.com/does-meteor-scale.html导致“如何扩展流星?” 文章http://meteorhacks.com/how-to-scale-meteor.html



文章来源: How efficient can Meteor be while sharing a huge collection among many clients?