性能改进

当您的应用程序开始增长时,如何优化以获得更高的性能。

本指南重点介绍如何提高 Meteor 应用性能(有时也称为扩展)的技巧和常见做法。需要注意的是,最终 Meteor 是一个与 MongoDB 紧密绑定的 Node.js 应用程序,因此您将遇到的许多问题在其他 Node.js 和 MongoDB 应用程序中也很常见。另外请注意,每个应用程序都不同,因此每个应用程序都面临着独特的挑战,因此本指南中描述的做法应作为指导方针而非绝对标准。

本指南深受 Marcin Szuster 的 Vazco 文章、官方 Meteor Galaxy 指南 以及 Paulo Mogollón 在 Impact 2022 上的演讲 “扩展 Meteor 实时数据的第一步” 的启发。

性能监控

在进行任何优化之前,我们需要知道我们的问题是什么。这就是 APM(应用程序性能监控)发挥作用的地方。如果您在 Galaxy 上托管,则它会自动包含在 专业版 中,您可以在其 专门的指南文章 中了解更多信息。对于在 Galaxy 之外托管的用户,最受欢迎的解决方案是使用 Monti APM,它与 Galaxy APM 共享所有主要功能。您也可以选择其他 Node.js APM,但它们不会向您显示 Galaxy APM 和 Monti APM 专注的 Meteor 特定数据。在本指南中,为了简单起见,我们将重点介绍如何使用 Galaxy APM,它与 Monti APM 相同。

设置这些 APM 之一后,您需要向您的 Meteor 应用程序添加一个包才能开始发送数据。

对于使用 Galaxy APM 和通过数据优化您的应用程序,请不要忘记访问 Meteor APM 指南

Galaxy APM

meteor add mdg:meteor-apm-agent

Monti APM

meteor add montiapm:agent

在 APM 中查找问题

APM 将首先为您提供应用程序性能的概述。然后,您可以深入了解发布、方法、发生的错误(客户端和服务器端)等的详细信息。您将花费大量时间在详细选项卡中查找需要改进的方法和发布,并分析您的操作的影响。例如,优化方法的过程如下所示
  1. 转到“方法”选项卡下的详细视图。
  2. 按响应时间对方法细分进行排序。
  3. 点击方法细分中的方法名称。评估如果您改进所选方法的影响。
  4. 查看响应时间图并找到跟踪。
  5. 如果您认为现在是时候这样做了,请改进您的方法。

并非每个性能较差的方法都需要改进。请查看以下示例

  • methodX - 平均响应时间 1 515 毫秒,吞吐量 100,05/分钟
  • methodY - 平均响应时间 34 000 毫秒,吞吐量 0,03/分钟

乍一看,34 秒的响应时间可能会引起您的注意,并且似乎 methodY 与改进更相关。但不要忽视这样一个事实,即该方法在几个小时内仅由系统管理员或计划的 cron 操作使用一次。

现在,让我们看看 methodX。它的响应时间明显更低,但与使用频率相比,它仍然很高,毫无疑问应该首先进行优化。

同样,绝对至关重要的是要记住,您不应该优化所有内容。关键是进行战略性思考,并将最关键的问题与您的产品优先级相匹配。

有关您可以在 Galaxy APM 中找到的所有内容的更多信息,请查看 Galaxy 指南 中的 Meteor APM 部分。

发布

发布允许使用 Meteor 最突出的方面,即实时数据。同时,这也是 Meteor 应用程序中最占用资源的部分。

在后台,WebSockets 正在使用 DDP 提供的其他功能。

正确使用发布

由于发布可能占用大量资源,因此应将其保留用于需要最新实时数据或频繁更改并且您需要用户看到这些更改的使用场景。您需要评估您的应用程序以确定哪些情况属于这种情况。根据经验,任何不需要实时或不经常更改的数据都可以通过其他方式获取一次,并在需要时重新获取,在大多数情况下,重新获取是不必要的。

但在您继续进行之前,您可以在此处进行一些改进。首先确保您只获取所需字段,将发送到客户端的文档数量限制为您需要的数量(即始终设置 limit 选项),并确保已设置所有索引。

方法优于发布 最简单的替代方法是使用 Meteor 方法而不是发布。在这种情况下,您可以使用现有的发布,而不是返回游标,而是调用 `.fetchAsync()` 并返回实际数据。这里也适用于使方法更快工作的相同性能改进,但是一旦调用,它就会发送数据,并且您没有发布的开销。

这里至关重要的是要确保您选择的前端框架不会每次都调用方法,而只调用一次来加载数据或在特定需要时调用(例如,当数据因用户操作而更新或用户请求时)。

发布替代

使用方法有其局限性,并且您可能希望评估其他工具作为潜在的替代方案。

Grapher 是一个受欢迎的答案,并允许您轻松地与另一个替代方案 GraphQL 特别是 Apollo GraphQL 结合使用,Apollo GraphQL 也与 Meteor 集成了 。最后,您也可以回到使用 REST。

请注意,您可以根据需要混合使用所有这些。

观察者重用率低

观察者是 Meteor 的关键组件之一。它们负责观察 MongoDB 上的文档并通知更改。创建它们是一个昂贵的操作,因此您需要确保 Meteor 尽可能地重用它们。

了解有关观察者的更多信息

观察者重用的关键是确保请求的查询相同。这意味着用户给定的值应该标准化,任何动态输入(如时间)也应该标准化。用户的发布应该首先检查用户是否已登录,然后返回发布,如果用户未登录,则应改为调用 this.ready();

了解有关改进观察者重用的更多信息

Redis Oplog

Redis Oplog 是 Meteor 的 Oplog 尾随(确保反应性,但有一些严重的限制,尤其会影响性能)的流行解决方案。顾名思义,Redis Oplog 使用 redis 来跟踪您仅需要的数据的更改并将其缓存。这减少了服务器和数据库的负载,允许您仅跟踪所需的数据并仅发布所需的更改。

方法

虽然方法被列为发布的可能替代方案之一,但它们本身可以变得更具性能,毕竟这确实取决于您在其中放入的内容,而 APM 将为您提供哪些方法是问题的必要见解。

繁重的操作

一般来说,需要大量资源或需要很长时间并阻塞服务器的操作应被提取出来,而应在其自己的服务器上运行,该服务器只专注于运行这些繁重的任务。这可以是另一个 Meteor 服务器,或者更好的是专门针对该特定任务进行优化的服务器。

重复性作业

重复性作业是另一个主要候选者,需要将其提取到其自己的应用程序中。这意味着您将拥有一个独立的服务器,该服务器将负责运行重复性作业,而主应用程序只会添加到列表中并接收结果,最可能是通过数据库结果。

速率限制

限制方法的速率以降低 DDoS 攻击的有效性并保护您的服务器。这也是确保您不会意外地对自身进行 DDoS 攻击的良好做法。例如,用户多次点击触发昂贵函数的按钮。在此示例中,您通常还应确保触发服务器事件的任何按钮在服务器返回事件已完成的响应之前都应处于禁用状态。

您可以并且应该限制方法和集合的速率。

了解有关限速的更多信息

MongoDB

以下部分提供了一些关于优化 Meteor 应用程序数据库性能的指导。您可以在其他处理 MongoDB 性能优化的资料中找到这些以及更多信息,例如在MongoDB 官方网站上。所有这些都适用,您也应该花一些时间研究它们。此指南提供了一些初始的和最常见的模式。

IP 白名单

如果您的 MongoDB 托管服务提供商允许,您应该确保将应用程序服务器的 IP 列入白名单。如果不这样做,您的数据库服务器可能会受到试图强行入侵的黑客攻击。除了安全风险之外,这还会影响性能,因为身份验证不是一个廉价的操作,它会影响性能。

请参阅Galaxy 指南了解有关 IP 白名单的信息,以获取 Galaxy 服务器的 IP。

索引

虽然在一个字段上的单个索引在简单的查询调用中很有帮助,但您很可能会有包含多个变量的更高级的查询。为了覆盖这些查询,您需要创建复合索引。例如

Statistics.createIndexAsync(
  {
    pageId: 1,
    language: 1,
    date: 1
  },
  { unique: true }
)

创建索引时,您应该按照 ESR(等值、排序、范围)样式对变量进行排序。这意味着,首先您放置将等于特定值的变量。其次,您放置排序变量,第三,您放置为该查询提供范围的变量。此外,您应该以过滤效果最大的字段排在最前面的方式对这些变量进行排序。

确保所有索引都被使用,并删除未使用的索引,因为保留未使用的索引会对性能产生负面影响,因为数据库仍然需要跟踪所有已索引的变量。

查找策略

为了优化查找,请确保所有查询都已建立索引。这意味着任何 .find() 变量都应如上所述建立索引。

所有查找都应该对返回结果进行限制,以便数据库在达到限制后停止遍历数据,并且您只返回有限数量的结果,而不是整个数据库。

注意 n + 1 问题相关的查询。例如,在包含汽车和汽车所有者的数据库中。您不希望获取汽车,然后为每个汽车所有者调用数据库,而是希望只使用两个查询。一个查询获取所有汽车,另一个查询获取所有所有者,然后在前端匹配数据。

检查所有运行时间超过 100 毫秒的查询,因为可能存在问题。

不要在查询中使用正则表达式,因为这些查询必须遍历所有数据才能进行匹配。

如果仍然存在问题,请确保您从辅助节点读取数据。

注意集合钩子

虽然集合钩子在许多情况下都能提供帮助,但请注意它们并确保您了解它们的工作原理,因为它们可能会创建您可能不知道的其他查询。确保检查使用它们的包,以便它们不会创建其他查询。

缓存

一旦您的用户群增长,您就需要投资查询缓存,例如使用 Redis、Redis Oplog 等。对于更复杂的查询或从多个集合检索数据时,您需要使用聚合并保存其结果。

扩展

垂直和水平扩展

主要有两种不同的扩展方式:垂直扩展和水平扩展。
  • 垂直扩展归结为为您的容器添加更多资源(CPU/RAM/磁盘),而水平扩展是指为您的资源池添加更多机器或容器。
  • 对于 Meteor 项目,水平扩展通常包括在一个具有多个内核的单个容器上运行应用程序的多个实例,或在多个容器上运行多个实例。

容器自动扩展

做好应对流量突然激增的准备非常重要。虽然这里提到的所有其他措施都会有所帮助,但在某个点上,在一个容器上支持更多用户变得不可能,需要添加其他容器来支持这些用户。如今,大多数托管解决方案都提供您可以设置的扩展触发器,以根据连接数、CPU 和 RAM 使用率等因素自动扩展(和缩减)应用程序的容器数量。Galaxy 也提供了这些功能。了解有关在 Galaxy 上设置扩展触发器的更多信息。

设置此功能至关重要,这样您的应用程序在有更多用户访问时可以继续运行,然后在容器未被使用时通过缩减规模节省资金。在最初设置这些功能时,请密切关注应用程序的性能。您需要了解何时是扩展应用程序的正确时机,以便它有足够的时间启动新容器,然后再有流量压垮现有容器等等。还有其他需要注意的事项。例如,如果您的应用程序被公司使用,您可能希望在工作日将容器的最小数量在工作时间开始前增加,然后在工作时间结束后和周末将最小数量减少到 1。

通常,当您处理性能问题时,您将拥有更多数量的容器,因为您正在优化应用程序。因此,在每轮改进之后重新检查您的扩展设置以确保扩展触发器得到正确优化至关重要。

在开发过程中,添加包来解决问题或支持某些功能非常诱人。这应该谨慎进行,并且每个包都应该仔细评估是否适合应用程序。除了安全和维护问题外,您还想知道给定包引入了哪些依赖项,以及整体上对性能的影响。

在 GitHub 上编辑
// 搜索框