[译] 为何要对生产环境的 Node.js 使用反向代理?

标签: 生产 环境 node | 发表时间:2020-01-30 10:54 | 作者:江米小枣tonylua
出处:https://juejin.im/welcome/backend

原文: medium.com/intrinsic/w…

早在 2012 年,PHP 和 Ruby on Rails 仍是统治渲染 web 应用的服务器端技术。但是,一个大胆的竞争者在社区中搅起风暴。这项在当年的一篇博文中宣称通过了百万级并发连接测试的技术并非其他,正是 Node.js,其流行程度从那时至今也在稳定增长。

不像当时的大多数同类技术,Node.js 自带一个 web server。拥有这个 server 意味着开发者可以绕过诸如 php.ini 的种种配置文件以及一个包含了若干 .htaccess 文件的层级结构。内置的 web server 同样提供了其他便利,如处理上传文件的能力或易于实现 WebSockets 等。

由 Node.js 驱动的 web 应用每天都顺畅地处理数以亿计的请求。世界上大多数大公司都在某些方面被 Node.js 影响。说 Node.js 可用于生产环境肯定是个保守的说法。但是,对于构建 Node.js 仍有一个适用的建议:

不应直接把一个 Node.js 进程暴露到 web 上,而应该将其隐藏在一个反向代理背后。

在我们揭示原因之前,先看看那是什么吧。

反向代理是什么?

一个反向代理(reverse proxy)基本上就是一种用于接受请求并将它们转发到某处的另一个 HTTP server 上的特殊 web server,也会接受响应并转发回给原始的请求者。

反向代理通常不会精确地发送原始请求,典型的做法是会在某些方面对请求作出修改。例如,如果反向代理位于 www.example.org:80 并转发请求到 ex.example.org:8080 的话,大概就是它重写了原始的 Host header 以匹配目标地址。反向代理也可能通过其他方式修改请求,比如清理一个有缺陷的请求或是在协议之间作出转换。

一旦反向代理收到一个响应,可能也会对其进行某方面的转换。常用的途径同样是修改 Host header 以匹配原始请求。

请求的 body 也能被修改。一种通常的修改是在响应时执行 gzip 压缩。另一种常见改变是当基础服务只是 HTTP 时启用 HTTPS 支持。

反向代理也可以将到来的请求分发给多个后端实例。如果一个服务暴露于 api.example.org,那么一个反向代理可以转发请求到 api1.internal.example.orgapi2.internal.example.org 等处。

有很多种不同的反向代理。两种更流行的分别是 NginxHAProxy。这两种工具都能够执行 gzip 压缩和增加 HTTPS 支持,它们也能很好地适用于其它领域。Nginx 更流行一些,并且也有诸如从文件系统运行静态文件服务器等一些其它有益的能力,所以我们将在本文中使用它作为例子。

现在我们知道 何为 反向代理了,下面来看看 为何 我们要将其用于 Node.js。

为何应该使用一个反向代理?

SSL 终端

SSL 终端是使用反向代理的最主要原因之一。将一个应用的协议从 http 变为 https 可不止添加一个 s 字母那么简单。Node.js 本身 能够https 执行必要的加密和解密,也能被配置为读取必要的证书文件。

但是,配置一个用于和我们的应用通信的协议,并管理一个永不过期的 SSL 凭证,并非真的是我们的应用需要关注的事情。检查一个代码库中的证书不只是麻烦,同时也是一种安全风险。在应用启动时从特定位置获取证书也有其风险。

有鉴于此,最好在应用之外执行 SSL 终端,通常就由一个反向代理来承担。受惠于像 certbot 这样的技术,通过 Nginx 维护证书也像设置一个定时任务一样简单。这样的一个任务可以自动安装新证书并动态重配置 Nginx 进程。与重启每个 Node.js 应用实例相比,这是一个破坏性小得多的过程。

同时,通过允许一个反向代理来执行 SSL 终端,这也意味着 只有 被反向代理的作者编写的代码可以访问你的私有 SSL 证书。反之,如果由你的 Node.js 应用处理 SSL,那么你的应用用到的每一个单独的第三方模块 -- 即便是潜在的恶意模块,都能访问你的私有 SSL 证书了。

gzip 压缩

gzip 是另一个你应该由应用让渡到反向代理的特性。gzip 压缩策略最好在管理级别设置,而不是不得不为每个应用指定和配置。

最好使用某些逻辑来决定对什么采用 gzip。例如,很小的文件,或许比 1kb 还小的,或许就不值得压缩,因为其 gzip 压缩版本没准会变得更大,亦或客户端对其解压的 CPU 开销也更不划算。同时,当处理二进制数据时,取决于其格式可能也无法从压缩中获益。有时 gzip 也无法被简单地启用或禁用,为了兼容压缩算法需要检查接收到的 Accept-Encoding header。

集群化

JavaScript 是一种单线程语言,相应的,Node.js 天然也是一种单线程的服务器平台(尽管 Node.js v10 中开始出现的实验性 worker 线程支持致力于改变这一点)。这意味着要从一个 Node.js 应用中获取尽可能更大的吞吐量需要运行和 CPU 核数差不多相同的实例数量。

Node.js 自带的 cluster 模块可以实现集群化。收到的 HTTP 请求将会被交给一个 master 进程而后再被分发给 cluster workers。

但是,动态伸缩 cluster workers 会有点费劲。通常也会通过运行一个额外的 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 压缩是两个高计算密集型的操作。专用型反向代理工具,如 Nginx 和 HAProxy,对这些操作术业有专攻,执行速度要快于 Node.js。用 Nginx 这样的 web server 从磁盘上读取静态内容也比 Node.js 来得快。有时甚至比起用额外的 Node.js 进程来执行集群化,用 Nginx 反向代理实现的效率都更高,内存和 CPU 的占用都更少。

但是,耳听为虚。让我们运行一些基准测试!

以下负载测试是使用 siege (译注:一个 http/https 回归测试和基准测试工具)执行的。我们用并发值 10 运行命令(模拟用户的请求),并且该命令会迭代运行 2 万次(总体会有 20 万次请求)。

为检验内存使用量我们在基准测试期间运行命令 pmap | grep total 若干次并取 平均值 作为结果(译注:Linux 中的 pmap 命令用于查看进程用了多少内存)。当使用单个 worker 线程运行 Nginx 时,最终会运行两个实例,一个是主线程,另一个是 worker 线程;然后将两个值相加。当运行一个包含 2 个节点的 Node.js 集群时,将有 3 个进程,一个是主进程,另两个是 worker 进程。下表中的 approx memory 列是给定测试中每个 Nginx 和 Node.js 进程的总和。

基准测试的结果

node-cluster 基准测试中我们使用了 2 个 worker,这意味着共有 3 个 Node.js 进程在运行:1 个 master 和 2 个 worker。在 nginx-cluster-node 基准测试中我们有 2 个 Node.js 进程在运行。每个 Nginx 测试都有一个单独的 Nginx 主进程和一个单独的 Nginx worker 进程。基准测试包括从磁盘读取一个文件,且无论是 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(译注:652 - 601 - 46.1)来作为 Node.js 的反向代理并架设从文件系统读取静态内容的服务。可以注意到一个有趣的事情是 Nginx 在其生命周期中保持了稳定的内存吞吐量;然而 Node.js 的内存吞吐量由于 JavaScript 天然的垃圾回收机制而时常起伏。

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

  • Nginx: 1.14.2
  • Node.js: 10.15.3
  • Siege: 3.0.8

测试执行在一台有 16GB 内存的机器上,有一块 i7-7500U CPU 4x2.70GHz,Linux 内核为 4.19.10。项目地址位于: github.com/IntrinsicLa…

简化应用代码

基准测试全面又出彩,但从我的观点来说,将部分工作从 Node.js 应用让渡给反向代理带来的最大收益是代码的简化。我们得以减少了大量可能造成潜在 bug 的必须应用代码并将其换成了声明式的配置。开发者中的一个普遍观点是他们对于让 Nginx 等外部团队编写这部分代码,而不是由他们自己编写更有信心。

不同于要安装和管理 gzip 压缩中间件并在多个 Node.js 项目中保持其最新,我们可以在一处统一配置它。和加载 SSL 证书后再重启应用进程不同,我们可以使用已有的证书管理工具。与其在应用中添加条件语句检查进程是 master 还是 worker,不如将其交给其它工具判断。

反向代理允许我们的应用聚焦于业务逻辑并忽略协议和进程管理。


尽管 Node.js 拥有运行在生产环境的完美能力,但将反向代理和生产环境的 HTTP Node.js 应用结合使用带来了种种收益。像 SSL 和 gzip 这样的操作变得更快。管理 SSL 证书也会更简单。大量所需应用代码也被减少了。



--End--

查看更多前端好文
请搜索 fewelife 关注公众号

转载请注明出处

相关 [生产 环境 node] 推荐:

什么是Node?

- We_Get - 博客园新闻频道
译者按:前不久Oreilly出了一本小册子“What is Node?”,扼要的讲解了Node的身世和所适用的场景,作者文笔轻松流畅、内容充实,是非常难得的学习资料.   译文全文:http://jayli.github.com/whatisnode/index.html.   作者:Brett McLaughlin ,原文:What is Node?.

Node入门

- - CSDN博客编程语言推荐文章
作者:  Manuel Kiessling. 翻译:  goddyzhao &  GrayZhang &  MondayChen. 本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识. 本书绝不是一本“Hello World”的教程. 你正在阅读的已经是本书的最终版.

浅析Hadoop Secondary NameNode,CheckPoint Node,Backup Node

- - CSDN博客云计算推荐文章
Hadoop SecondaryNameNode并不是Hadoop 第二个NameNode,它不提供NameNode服务,而仅仅是NameNode的一个工具. 这个工具帮助NameNode管理Metadata数据. NameNode的HDFS文件信息(即Metadata)记录在内存中,client的文件写操作直接修改内存中的Metadata,同时也会记录到硬盘的Edits文件,这是一个Log文件.

[译]什么是Node?

- blacktulip - Taobao UED Team
译者按:前不久Oreilly出了一本小册子“What is Node?”,扼要的讲解了Node的身世和所适用的场景,作者文笔轻松流畅、内容充实,是非常难得的学习资料. 译文全文:http://jayli.github.com/whatisnode/index.html. 作者:Brett McLaughlin ,原文:What is Node?.

在生产环境运行容器

- - IT瘾-tuicool
【编者的话】Vivek Juneja是一名工作首尔的云服务工程师. 他从2008年就开始接触云服务,是最早的AWS和Eucalyptus的使用者. 本文中总结了在生产环境中使用容器的几个方面,特别是对虚拟机与容器的混合部署的观点很值得推荐给大家. 如果只是把容器限制在开发测试环境中,那么您并没有享受到面向容器研发和发布工作的全部红利.

用node作桌面开发

- InterMa - CNode社区
node的定位是,server-side javascript. 但程序员最爱做的事,就是把一个东西用在不该用的地方. 那么,可以把node用在桌面开发上吗. 把Javascript用在桌面开发上,早有先例,比如GTK+的gjs,还有Qt的QML(顺带一提,QML代表着桌面开发的另一个方向,a promising way),GNOME3中,也用javascript作为桌面插件的开发语言.

node js 断点调试

- - Web前端 - ITeye博客
大部分基于 Node.js 的应用都是运行在浏览器中的,. 例如强大的调试工具 node-inspector. node-inspector 是一个完全基于 Node.js 的开源在线调试工具,提供了强大的调试功能和友好. 的用户界面,它的使用方法十分简便. 首先,使用 npm install -g node-inspector 命令安装 node-inspector,然后在终.

Vercel 部署 Node 服务

- - 掘金 前端
之前在写 面试常客:HTTP 缓存时,曾经就强缓存和协商缓存写过两个demo,但缓存要在服务端做,只能贴上代码,不能在网页上感受(虽然我贴了gif). 笔者的所有 demo 例子都放在 github page 上,其特点是不需要服务器即可部署静态资源,但它不具备部署服务端应用能力. 最近笔者在了解 CI/CD 方面的知识点,想起了 Vercel,就想着能否将服务端应用架在 vercel 上呢.

MySQL生产环境突发故障处理手册

- gOODiDEA - MySQL OPS
1.2 碎片整理和统计信息更新 OPTIMIZE 操作等于recreate + analyze 的组合操作,所以会堵塞更新类型SQL语句. 对于备机上跑只读类型操作的业务,可以考虑使用此操作命令,对于主服务器不建议使用此命令,为此备机上执行OPTIMIZE 语句,必须这样写: [...].