Fla*_*ken 100 benchmarking meteor
想象一下以下情况:
1,000个客户端连接到显示"Somestuff"集合内容的Meteor页面.
"Somestuff"是一个收藏1,000件物品的系列.
有人在"Somestuff"集合中插入一个新项目
会发生什么:
Meteor.Collection客户端上的所有内容都将更新,即插入转发给所有客户端(这意味着向1,000个客户端发送一条插入消息)服务器确定哪个客户端需要更新的CPU成本是多少?
是否准确只将插入的值转发给客户端,而不是整个列表?
这在现实生活中如何运作?是否有任何这种规模的基准或实验?
deb*_*lis 119
简短的回答是只有新数据才能通过网络发送.这是它的工作原理.
Meteor服务器有三个重要的部分来管理订阅:发布功能,它定义订阅提供的数据的逻辑; 的蒙戈驱动器,其手表更改数据库中; 和合并框,它结合了所有客户端的有效订阅,并通过网络将它们发送到客户端.
每次Meteor客户端订阅集合时,服务器都会运行
发布功能.发布功能的工作是找出其客户端应具有的文档集,并将每个文档属性发送到合并框中.它为每个新的订阅客户端运行一次.您可以在发布函数中放置您想要的任何JavaScript,例如使用任意复杂的访问控制this.userId.发布函数调用将数据发送到合并框this.added,this.changed和
this.removed.有关详细信息,请参阅
完整发布文档.
大多数的发布功能,没有淤泥与周围的低级别
added,changed和removedAPI,虽然.如果发布函数返回一个蒙戈光标,流星服务器自动蒙戈驱动程序(的输出连接insert,update以及removed回调)到合并框的输入(this.added,this.changed和this.removed).您可以在发布功能中预先执行所有权限检查,然后直接将数据库驱动程序连接到合并框,而不需要任何用户代码.当打开自动发布时,即使隐藏了这一点:服务器会自动为每个集合中的所有文档设置查询,并将它们推送到合并框中.
另一方面,您不仅限于发布数据库查询.例如,您可以编写一个发布函数,该函数从内部设备读取GPS位置Meteor.setInterval,或者从其他Web服务轮询旧版REST API.在这种情况下,你会通过调用低级别发出更改合并盒added,changed和removedDDP API.
该蒙戈司机的工作是看Mongo的数据库更改居住查询.这些查询连续运行,并通过调用返回的更新结果的变化added,removed和changed回调.
Mongo不是实时数据库.所以司机民意调查.它为每个活动的实时查询保留最后查询结果的内存中副本.在每个轮询周期,它的新的结果与以前保存的结果进行比较,计算最低限度的added,removed和changed
事件描述的差别.如果多个调用者为同一个实时查询注册回调,则驱动程序只会查看查询的一个副本,并使用相同的结果调用每个已注册的回调.
每次服务器更新集合时,驱动程序都会重新计算该集合上的每个实时查询(未来版本的Meteor将公开一个扩展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回调中的每一个.
接下来,发布功能.这里几乎没有发生:1000个insert回调中的每一个都通过调用将数据推送到合并框中added.
最后,每个合并框都会根据其客户端缓存的内存中副本检查这些新属性.在每种情况下,它都会发现值尚未在客户端上,并且不会影响现有值.因此合并箱发出DDP DATA的SockJS连接到它的客户端上的消息,并更新其服务器端的内存拷贝.
总CPU成本是差异一个Mongo查询的成本,加上1,000个合并框检查其客户端状态和构建新DDP消息有效负载的成本.通过网络流动的唯一数据是发送到1,000个客户端中的每个客户端的单个JSON对象,对应于数据库中的新文档,以及从创建原始插入的客户端到服务器的一条RPC消息.
这是我们一定有的计划.
更高效的Mongo驱动程序.我们 在0.5.1中优化了驱动程序,每个不同的查询只运行一个观察者.
并非每个数据库更改都应触发重新计算查询.我们可以进行一些自动改进,但最好的方法是让开发人员指定需要重新运行哪些查询的API.例如,对于开发人员来说显而易见的是,将消息插入一个聊天室不应该使第二个房间中的消息的实时查询无效.
Mongo驱动程序,发布函数和合并框不需要在同一进程中运行,甚至不需要在同一台机器上运行.某些应用程序运行复杂的实时查询,需要更多CPU来观察数据库.其他人只有几个不同的查询(想象一个博客引擎),但可能有很多连接的客户端 - 这些需要更多的CPU用于合并框.分离这些组件将让我们独立地扩展每个部分.
许多数据库支持在更新行时触发的触发器并提供旧行和新行.使用该功能,数据库驱动程序可以注册触发器而不是轮询更改.
And*_*Mao 29
根据我的经验,在Moteor中共享大量集合时使用许多客户端基本上是不可行的,从版本0.7.0.1开始.我会试着解释原因.
如上文所述以及https://github.com/meteor/meteor/issues/1821中所述,流星服务器必须为合并框中的每个客户端保留已发布数据的副本.这是允许Meteor魔法发生的原因,但也导致任何大型共享数据库被重复保存在节点进程的内存中.即使对静态集合使用可能的优化,例如in(有没有办法告诉meteor集合是静态的(永远不会改变)?),我们遇到了Node进程的CPU和内存使用量的巨大问题.
在我们的例子中,我们向每个客户发布了一个完全静态的15k文档集合.问题是在连接时将这些文档复制到客户端的合并盒(在内存中)基本上将节点进程带到100%CPU几乎一秒钟,并导致大量额外的内存使用.这本质上是不可扩展的,因为任何连接客户端都会使服务器瘫痪(并且同时连接会相互阻塞),并且内存使用量将在客户端数量上呈线性增长.在我们的例子中,即使传输的原始数据只有大约5MB ,每个客户端也会造成大约60MB的内存使用量.
在我们的例子中,因为集合是静态的,我们通过将所有文档作为.json文件发送来解决这个问题,该文件由nginx gzip,并将它们加载到匿名集合中,导致只有大约1MB的数据传输而没有额外的CPU节点进程中的内存或内存以及更快的加载时间.该系列的所有操作都是通过使用_id服务器上较小的出版物中的s来完成的,从而保留了Meteor的大部分优点.这使得应用程序可以扩展到更多客户端.此外,由于我们的应用程序主要是只读的,我们通过负载平衡(尽管使用单个Mongo)在nginx后面运行多个Meteor实例,进一步提高了可伸缩性,因为每个Node实例都是单线程的.
但是,在多个客户端之间共享大型可写集合的问题是一个需要由Meteor解决的工程问题.可能有比保留每个客户端的所有内容副本更好的方法,但这需要一些认真的思考作为分布式系统问题.目前大量CPU和内存使用的问题不会扩展.
| 归档时间: |
|
| 查看次数: |
11136 次 |
| 最近记录: |