如何在Node.js中优化服务器端渲染?

作者|BenHughes译者|无明在Airbnb,我们花了数年时间将所有前端代码迁移到React架构,RubyonRails在Web应用中所占的比例每天都在减少。实际上,我们很快会转向另一个新的服务,即通过Node.js提供完整的服务器端渲染页面。这个服务将为Airbnb的所有产品渲染大部分HTML。

如果fn1和fn2是计算密集型的,它们将像这样执行:

一个操作必须等待另一个操作完成后才能运行,因为只有一个执行线程。在进行服务器端渲染时,当服务器进程需要处理多个并发请求,就会出现这种情况。正在处理中的请求将导致其他请求延迟:

在实际当中,请求通常由许多不同的异步阶段组成,尽管仍然以计算为主。这可能导致更糟糕的交叉。如果我们的请求包含一个像renderPromise().then(out=>formatResponsePromise(out)).then(body=>res.send(body))这样的链,那么请求的交叉可能是这样的:

在这种情况下,两个请求都需要两倍的时间才能处理完成。随着并发的增加,这个问题将变得更加严重。SSR的一个目标是能够在客户端和服务器上使用相同或类似的代码。这两种环境之间存在一个巨大的差异,客户端上下文本质上是单租户的,而服务器上下文却是多租户的。在客户端可以正常运行的东西,比如单例或全局状态,到了服务器端就会导致bug、数据泄漏和各种混乱。这两个问题都与并发有关。

用户请求进入我们的Rails主应用程序Monorail,它为需要进行渲染的React组件组装props,并向Hypernova发送带有这些props和组件名称的请求。Hypernova使用收到的props来渲染组件,生成HTML并返回给Monorail,Monorail将HTML片段嵌入到页面模板中,并将所有内容发送给客户端。

如果Hypernova渲染失败(由于错误或超时),就将组件及props嵌入页面,或许它们可以成功地在客户端渲染。因此,我们认为Hypernova是一个可选的依赖项,我们能够容忍一些超时和失败。我根据SLAp95来设置超时时间,不出所料,我们的超时基线略低于5%。在高峰流量负载期间进行部署时,我们可以看到从Monorail到Hypernova最多有40%的请求超时。

部署超时峰值示例(红线)我们把这些超时和错误归因于缓慢的启动时间,如GC启动初始化、缺少JIT、填充缓存等等。新发布的React或Node有望提供足够的性能改进,以缓解启动缓慢的问题。我怀疑这可能是由于不良的负载均衡或部署期间的容量问题造成的。当我们在同一个进程上同时运行多个计算请求时,我们看到了延迟的增加。我添加了一个中间件来记录进程同时处理的请求数。

我们将启动延迟归咎于并发请求等待CPU。从我们的性能指标来看,我们无法区分用于等待执行的时间与用于实际处理请求的时间。这也意味着并发性带来的延迟与新代码或新特性带来的延迟是相同的——这些实际上都会增加单个请求的处理成本。很明显,我们不能将BadRequestError:Requestaborted错误归咎于启动延迟。

我们决定使用两个现有的组件来解决这个问题:反向代理(Nginx)和负载均衡器(HAProxy)。反向代理和负载均衡为了充分利用Hypernova实例上的多核CPU,我们在单个实例上运行多个Hypernova进程。因为这些是独立的进程,所以能够并行处理并发请求。

问题是每个Node进程将在整个请求时间内被占用,包括从客户端读取请求消息体。虽然我们可以在单个进程中并行读取多个请求,但在渲染时,这会导致计算操作交叉。因此,Node进程的使用情况取决于客户端和网络的速度。解决办法是使用缓冲反向代理来处理与客户端的通信。为此,我们使用了Nginx。Nginx将客户端的请求读入缓冲区,并在完全读取后将完整请求传给Node服务器。

通过使用Nginx来处理读取请求,我们能够实现更高的Node进程利用率。

我们还使用Nginx来处理一部分请求,不需要将它们发送给Node.js进程。我们的服务发现和路由层通过/ping低成本请求来检查主机之间的连接性。在Nginx中处理这些可以降低Node.js进程的吞吐量。接下来是负载均衡。我们需要明智地决定哪些Node.js进程应该接收哪些请求。

但是当有不同类型的请求需要花费不同的处理时间时,它就不那么好用了。后面的请求必须等待前面的请求全部完成,即使有另一个进程可以处理它们。

更好的分发模型应该像这样:

因为这可以最大限度地减少等待时间,并可以更快地返回响应。这可以通过将请求放进队列中并只将请求分配给空闲的进程来实现。为此,我们使用了HAProxy。

当我们在Hypernova中实现了这些,就完全消除了部署时的超时峰值以及BadRequestError错误。并发请求也是造成延迟的主要因素,随着新方案的实施,延迟也降低了。在使用相同的超时配置的情况下,超时率基线从5%变为2%。部署期间的40%失败也降低到了2%,这是一个重大的胜利。现在,用户看到空白页的几率已经很低了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180726A02X3S00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励