这一年是2012年.PHP和Ruby on Rails作为渲染Web应用程序的最高服务器端技术而备受瞩目。但是,一个大胆的新竞争者掀起了一场风暴 - 一个能够处理1M并发连接的人。这项技术不过是Node.js,从那以后一直稳步增长。

与当时大多数竞争技术不同,Node.js内置了一个Web服务器。拥有这个服务器意味着开发人员可以绕过无数的配置文件,例如文件php.ini的分层集合
.htaccess。拥有内置的Web服务器还提供了其他便利,例如在上载文件时处理文件的能力以及实现WebSockets的简易性。

每天Node.js驱动的Web应用程序都会愉快地处理数十亿个请求。世界上大多数最大的公司都以Node.js的某种方式供电。说Node.js是生产就绪的当然是轻描淡写。但是,自Node.js诞生以来,有一条建议是正确的:不应该直接将Node.js进程暴露给Web,而应该隐藏在反向代理之后。但是,在我们搞清楚为什么要使用反向代理之前,让我们首先看一下它是什么。

什么是反向代理?

反向代理基本上是一种特殊类型的Web服务器,它接收请求,将它们转发到其他地方的另一个HTTP服务器,接收回复,并将回复转发给原始请求者。

但是,反向代理通常不会发送确切的请求。通常,它会以某种方式修改请求。例如,如果反向代理服务于www.example.org:80,并且要将请求转发给
ex.example.org:8080它,它可能会重写原始Host标头以匹配目标标头。它还可以通过其他方式修改请求,例如清除格式错误的请求或在协议之间进行转换。

一旦反向代理接收到响应,它就可以以某种方式转换该响应。同样,常见的方法是修改Host标头以匹配原始请求。请求的主体也可以更改。常见的修改是对响应执行gzip压缩。另一个常见的变化是在底层服务只支持HTTP时启用HTTPS支持。

反向代理也可以将传入的请求分派给多个后端实例。如果服务是在暴露api.example.org,反向代理可以将请求转发给api1.internal.example.orgapi2

那里有许多不同的反向代理。其中两个比较受欢迎的是NginxHAProxy。这两个工具都能够执行gzip压缩并添加HTTPS支持,并且它们也专注于其他领域。Nginx是两种选择中比较流行的,并且还具有一些其他有益的功能,例如从文件系统提供静态文件的能力,因此我们将在本文中使用它作为示例。

既然我们知道反向代理是什么,我们现在可以看看为什么我们想要使用Node.js。

我为什么要使用反向代理?

SSL终止

SSL终止是使用反向代理的最常见原因之一。从改变那些应用程序的协议http,以https不是追加的多一点的工作s。Node.js的本身能够执行进行必要的加密和解密https,并且可以配置为读取所需的证书文件。

但是,配置用于与我们的应用程序通信的协议以及管理过期的SSL证书并不是我们的应用程序需要关注的问题。将证书检入代码库不仅繁琐,而且还存在安全风险。在应用程序启动时从中心位置获取证书也存在风险。

因此,最好在应用程序之外执行SSL终止,通常在反向代理中执行。感谢像Let's Encrypt这样certbot的技术,使用Nginx维护证书就像设置一个cron作业一样简单。这样的作业可以自动安装新证书并动态重新配置Nginx进程。这是一个破坏性较小的过程,然后重新启动每个Node.js应用程序实例。

此外,通过允许反向代理执行SSL终止,这意味着只有反向代理作者编写的代码才能访问您的私有SSL证书。但是,如果您的Node.js应用程序正在处理SSL,那么您的应用程序使用的每个第三方模块(甚至可能是恶意模块  )都可以访问您的私有SSL证书。

gzip压缩

gzip压缩是另一个应该从应用程序卸载到反向代理的功能。gzip压缩策略是在组织级别最好设置的,而不必为每个应用程序指定和配置。

在决定gzip的内容时最好使用一些逻辑。例如,非常小,可能小于1kb的文件可能不值得压缩,因为gzip压缩版本有时可能更大,或者让客户端解压缩文件的CPU开销可能不值得。此外,在处理二进制数据时,根据格式,它可能无法从压缩中受益。gzip也是无法简单启用或禁用的东西,它需要检查传入的Accept-Encoding头以获得兼容的压缩算法。

cluster

JavaScript是一种单线程语言,因此,Node.js传统上是一个单线程服务器平台(但是,Node.js v10中目前实验性的工作线程支持旨在改变这一点)。这意味着从Node.js应用程序获得尽可能多的吞吐量需要运行与CPU核心大致相同数量的实例。

Node.js带有内置cluster模块,可以做到这一点。将向主进程发送传入的HTTP请求,然后将其分派给集群工作程序。

但是,动态扩展集群工作人员需要付出一些努力。在调度主进程中运行额外的Node.js进程时,通常还会增加开销。此外,跨不同计算机的扩展过程是cluster无法做到的。

出于这些原因,有时最好使用反向代理来分派运行Node.js进程的请求。这些反向代理可以动态配置为在新应用程序到达时指向它们。实际上,应用程序应该只关注自己的工作,它不应该关心管理多个副本和分派请求。

企业路由

在处理大型Web应用程序(例如由多团队企业构建的应用程序)时,使用反向代理来确定将请求转发到何处非常有用。例如,example.org/search/*可以将发出的请求路由到内部搜索应用程序,同时example.org/profile/*可以将其他请求分派到内部配置文件应用程序。

这样的工具允许其他强大的功能,如粘性会话,蓝/绿部署,A / B测试等。我个人在代码库中工作,在应用程序中执行此类逻辑,这种方法使应用程序很难维护。

性能优势

Node.js具有很强的可塑性。它能够从文件系统提供静态资源,使用HTTP响应执行gzip压缩,内置支持HTTPS以及许多其他功能。它甚至能够通过模块运行应用程序的多个实例并执行自己的请求调度cluster

然而,最终让反向代理为我们处理这些操作符合我们的最佳利益,而不是让我们的Node.js应用程序执行它。除了上面列出的每个原因之外,想要在Node.js之外进行这些操作的另一个原因是效率。

SSL加密和gzip压缩是两个高度CPU绑定的操作。专用的反向代理工具,如Nginx和HAProxy,通常比Node.js更快地执行这些操作。像Nginx这样的Web服务器从磁盘读取静态内容也会比Node.js更快。甚至群集有时也会更有效,因为像Nginx这样的反向代理将使用比其他Node.js进程更少的内存和CPU。

但是,不要相信我们的话。我们来做一些基准吧!

使用以下进行以下负载测试siege。我们使用并发值10(同时发出10个请求的用户)运行命令,命令将运行直到进行20,000次迭代(对于200,000个总体请求)。

为了检查内存,我们pmap <pid> | grep total在基准测试的整个生命周期中运行命令几次,然后平均结果。当使用单个工作线程运行Nginx时,最终会运行两个实例,一个是主服务器,另一个是工作服务器。然后我们将这两个值相加。当运行Node.js集群为2时,将有3个进程,一个是主进程,另外两个是工作进程。下表中的近似内存列是给定测试的每个Nginx和Node.js过程的总和。

以下是基准测试的结果:

基准测试结果

node-cluster基准测试中,我们使用2个worker。这意味着有3个Node.js进程在运行:1个master和2个worker。在nginx-cluster-node基准测试中,我们运行了2个Node.js进程。每个Nginx测试都有一个Nginx主服务器和一个Nginx工作进程。基准测试涉及从磁盘读取文件,Nginx和Node.js都没有配置为将文件缓存在内存中。

使用Nginx为Node.js执行SSL终止会导致吞吐量增加约16%(749rps到865rps)。使用Nginx执行gzip压缩会导致吞吐量增加约50%(5,047rps至7,590rps)。使用Nginx管理进程集群导致性能损失约-1%(8,006rps到7,908rps),这可能是由于在环回网络设备上传递额外请求的开销。

基本上,单个Node.js进程的内存使用量约为600MB,而Nginx进程的内存使用量约为50MB。根据所使用的功能,这些可能会略微波动,例如,Node.js 在执行SSL终止时使用额外的~13MB,而当用作反向代理时,Nginx使用额外的~4MB来提供来自文件系统的静态内容。值得注意的一件事是Nginx在其整个生命周期中使用了一致的内存量。但是,由于JavaScript的垃圾收集性质,Node.js不断波动。

以下是执行此基准测试时使用的软件版本:

  • Nginx的: 1.14.2
  • Node.js的: 10.15.3
  • 围城: 3.0.8

测试是在具有16GB内存,i7-7500U CPU 4x2.70GHzLinux和Linux内核的机器上进行的4.19.10。重新创建上述基准测试所需的所有必要文件均可在此处获得:
IntrinsicLabs / nodejs-reverse-proxy-benchmarkmarks

简化的应用程序代码

基准测试很好,但在我看来,将工作从Node.js应用程序卸载到反向代理的最大好处是代码简单。我们可以减少潜在错误的命令式应用程序代码的行数,并将其交换为声明性配置。开发人员普遍认为,他们对由外部工程师团队(如Nginx)编写的代码比对自己编写的代码更有信心。我们可以在一个位置配置它,而不是安装和管理gzip压缩中间件并使其在各种Node.js项目中保持最新。我们可以改为使用现有的证书管理工具,而不是运送或下载SSL证书,重新获取或重新启动应用程序流程。我们可以将其卸载到另一个工具,而不是将条件添加到我们的应用程序以检查进程是主进程还是工作进程。反向代理允许我们的应用程序专注于业务逻辑并忘记协议和流程管理。


尽管Node.js完全能够在生产中运行,但使用具有生产HTTP Node.js应用程序的反向代理提供了无数的好处。SSL和gzip等操作变得更快。SSL证书的管理可以变得更简单。所需的应用程序代码量也减少了。我强烈建议您在下一个生产Node.js应用程序时使用反向代理。


查看英文原文

查看更多文章

公众号:银河系1号

联系邮箱:public@space-explore.com

(未经同意,请勿转载)