早在2012年,PHP和RubyonRails仍然是渲染Web应用程序的主要服务器端技术。但是一个大胆的竞争者正在社区中掀起一场风暴。当年在一篇博文中声称通过百万并发连接测试的技术非Node.js莫属,此后其热度稳步上升。与当时大多数类似技术不同,Node.js附带了一个Web服务器。拥有此服务器意味着开发人员可以绕过php.ini等配置文件和.htaccess文件的层次结构。内置的Web服务器还提供其他便利,例如处理上传文件的能力或轻松实施WebSockets。由Node.js提供支持的Web应用程序每天无缝处理数亿个请求。世界上大多数大公司都在某种程度上受到了Node.js的影响。说Node.js已准备好用于生产使用是一种轻描淡写的说法。然而,对于构建Node.js仍然有一个有效的建议:不要将Node.js进程直接暴露给网络,而是将其隐藏在反向代理后面。在我们揭示原因之前,让我们先看看那是什么。什么是反向代理?反向代理(reverseproxy)基本上是一个特殊的网络服务器,它接受请求并将它们转发到某处的另一个HTTP服务器,同时也接受响应并将它们转发回原来的请求者。反向代理通常不发送准确的原始请求,通常以某种方式修改请求。例如,如果反向代理位于www.example.org:80并将请求转发到ex.example.org:8080,它可能会重写原始主机标头以匹配目标地址。反向代理还可以以其他方式修改请求,例如清理有缺陷的请求或在协议之间进行转换。一旦反向代理收到响应,它也可能以某种方式对其进行转换。一种常见的方法也是修改Host标头以匹配原始请求。请求正文也可以修改。一个常见的修改是对响应执行gzip压缩。另一个常见的更改是在底层服务仅为HTTP时启用HTTPS支持。反向代理还可以将传入请求分发到多个后端实例。如果服务在api.example.org公开,则反向代理可以将请求转发到api1.internal.example.org、api2.internal.example.org等。反向代理有很多种。其中两个比较流行的是Nginx和HAProxy。这两种工具都执行gzip压缩并添加HTTPS支持,并且它们在其他领域也能很好地工作。Nginx更受欢迎并且具有一些其他有用的功能,例如从文件系统运行静态文件服务器,因此我们将在本文中将其用作示例。现在我们知道了什么是反向代理,让我们看看为什么我们将它与Node.js一起使用。为什么要使用反向代理?SSL终止SSL终止是使用反向代理的首要原因之一。将应用程序的协议从http更改为https不仅仅是添加一个s。Node.js本身可以对https进行必要的加解密,也可以配置读取必要的证书文件。然而,配置与我们的应用程序通信的协议和管理永不过期的SSL证书并不是我们的应用程序真正需要关心的事情。在代码库中检查证书不仅麻烦,而且存在安全风险。在应用程序启动时从特定位置获取证书也有其风险。因此,最好在应用程序外部执行SSL终止,通常由反向代理执行。由于certbot等技术,使用Nginx维护证书就像设置cron作业一样简单。这样的任务可以自动安装新证书并动态重新配置Nginx进程。与重新启动Node.js应用程序的每个实例相比,这个过程的破坏性要小得多。此外,通过允许反向代理执行SSL终止,这也意味着只有反向代理作者编写的代码才能访问您的私有SSL证书。相反,如果您的Node.js应用程序处理SSL,那么您的应用程序使用的每个第三方模块——甚至是潜在的恶意模块——都将有权访问您的私有SSL证书。gzip压缩gzip是您应该从应用程序中放弃给反向代理的另一个特性。gzip压缩策略最好在管理级别设置,而不是必须为每个应用程序指定和配置。最好使用一些逻辑来决定要gzip的内容。例如,非常小的文件,可能小于1kb,可能不值得压缩,因为gzip版本可能会更大,或者客户端会以更少的CPU开销解压缩它。此外,在处理二进制数据时,根据其格式,它也可能无法从压缩中获益。有时不能简单地启用或禁用gzip。为了兼容压缩算法,需要检查接收到的Accept-Encoding头。集群JavaScript是一种单线程语言,因此,Node.js是一个自然的单线程服务器平台(尽管从Node.jsv10开始的实验性工作线程支持旨在改变这一点)。这意味着要从Node.js应用程序中获得最大可能的吞吐量,需要运行与CPU内核数量大致相同的实例。Node.js自带的cluster模块可以实现集群。接收到的HTTP请求将被移交给一个主进程,然后分发给集群工作进程。然而,动态扩展集群工作者可能有点费力。通常还可以通过运行一个额外的Node.js进程作为主要分发进程来增加吞吐量。但是,集群跨机器扩展进程仍然有点困难。由于这些原因,有时最好使用反向代理来分发正在运行的Node.js进程。反向代理可以动态配置以指向新出现的应用程序进程。事实上,一个应用程序应该只关心它自己的工作,而不是管理多个副本和分发请求。企业路由在启动大型Web应用程序时,尤其是由具有多个团队的企业创建的应用程序,使用反向代理来决定如何转发请求非常有用。例如,对example.org/search/*的请求可以路由到内部搜索应用程序,而对example.org/profile/*的其他请求可以路由到内部配置文件应用程序。这种处理提供了其他强大的功能,例如粘性会话、蓝/绿部署、A/B测试等。我个人曾在一个代码库中工作,其中逻辑由应用程序实现,这种实现使得应用程序极难维护。性能提升Node.js具有高度的可塑性。它可以从文件系统提供静态资产,对HTTP响应执行gzip压缩,内置对HTTPS的支持,以及许多其他功能。它甚至能够运行应用程序的多个实例并通过集群模块分发自己的请求。但是,让反向代理处理这些操作而不是依赖Node.js应用程序来执行这些操作基本上符合我们的利益。除了上面列出的原因之外,想要在Node.js之外执行这些操作的另一个原因是效率。SSL加密和gzip压缩是两个高度计算密集型操作。专用的反向代理工具,例如Nginx和HAProxy,专门处理这些操作并且比Node.js执行得更快。使用像Nginx这样的Web服务器从磁盘读取静态内容也比Node.js更快。有时使用Nginx反向代理实现集群甚至比使用额外的Node.js进程执行集群更有效,内存和CPU使用率更低。然而,听说是假的。让我们运行一些基准测试!以下负载测试是使用siege执行的。我们运行并发值为10的命令(模拟用户请求),命令迭代20,000次(总共200,000个请求)。要检查内存使用情况,我们运行命令pmap|greptotal在benchmark期间多次并取平均值作为结果。当使用单个工作线程运行Nginx时,您最终会运行两个实例,一个作为主线程,另一个作为工作线程;然后将这两个值相加。当运行一个有2个节点的Node.js集群时,会有3个进程,一个是master进程,另外两个是worker进程。下表中的approxmemory列是给定测试中每个Nginx和Node.js进程的总和。基准测试结果在节点集群基准测试中,我们使用了2个worker,这意味着有3个Node.js进程在运行:1个master和2个worker。在nginx-cluster-node基准测试中,我们有2个Node.js进程在运行。每个Nginx测试都有一个单独的Nginxmaster进程和一个单独的Nginxworker进程。基准测试包括从磁盘读取文件,Nginx和Node.js都配置为在内存中缓存文件。使用Nginx为Node.js执行SSL终止导致吞吐量增加约16%(746rps到865rps)。使用Nginx执行gzip压缩将吞吐量提高了大约50%(5,047rps到7,590rps)。使用Nginx管理进程集群导致约1%(8,006rps到7,908rps)的性能损失,这可能是由于在环回网络设备之间传递额外请求的开销。基本的Node.js单进程单内存使用量约为600MB,而Nginx进程约为50MB。这些用法根据使用的功能略有波动,例如,Node.js使用额外的~13MB用于SSL终止,Nginx使用额外的4MB用于Node.js并设置从文件系统读取静态内容的服务。需要注意的一件有趣的事情是Nginx在其整个生命周期内保持稳定的内存吞吐量;然而,由于JavaScript的天然垃圾回收机制,Node.js的内存吞吐量会不时波动。以下是用于执行此基准测试的软件版本:Nginx:1.14.2Node.js:10.15.3Siege:3.0.8测试在具有16GB内存的机器上执行,具有i7-7500UCPU4x2.70GHz,Linux内核是4.19.10。项目地址位于:github.com/IntrinsicLa...。简化的应用程序代码基准测试是全面而精彩的,但从我的角度来看,将一些工作从Node.js应用程序卸载到反向代理的最大好处是代码的简化。我们能够减少大量可能存在错误的应用程序代码,并将其替换为声明式配置。开发人员之间的一个普遍看法是,他们更有信心让Nginx等外部团队编写这部分代码,而不是自己编写。无需安装和管理gzip压缩中间件并使其在多个Node.js项目中保持最新,我们可以在一个地方集中配置它。不同于加载SSL证书后重启申请流程,我们可以使用现有的证书管理工具。与其在应用程序中添加条件语句来检查进程是master还是worker,不如交给其他工具来判断。反向代理允许我们的应用程序专注于业务逻辑而忽略协议和流程管理。虽然Node.js完全能够在生产环境中运行,但在生产HTTPNode.js应用程序中使用反向代理也有好处。SSL和gzip等操作变得更快。管理SSL证书也将更加容易。所需的大量应用程序代码也减少了。作者:江米小枣tonylua链接:https://juejin.im/post/5e3015af5188254c23704ee1
