“如果没有将#nginx部署在node服务器前端,那您的做法可能不太对。”——Bryan Hughes在Twitter上表示
Node.js是使用JavaScript创建服务器应用程序的主流工具。JavaScript是全球广受欢迎的编程语言之一。Node.js兼具Web服务器和应用服务器的功能,如今已成为各类基于微服务的开发和交付工作中的关键工具。(免费下载Forrester的Node.js与NGINX报告。)
在后端应用开发中,Node.js可以取代或补充Java或.NET。
Node.js采用单线程和非阻塞I/O,因此可以扩展并支持数万项并发操作。它与NGINX具有相同的架构特征,并解决了C10K问题,即支持超过10,000个并发连接;NGINX最初也是为解决这一问题而开发的。Node.js以高性能和有助于提升开发者工作效率而闻名。
那么,可能会出什么问题呢?
Node.js存在一些薄弱环节和漏洞,可能导致基于Node.js的系统性能不佳,甚至崩溃。当基于Node.js的Web应用流量快速增长时,这些问题会更频繁地出现。
此外,Node.js非常适合创建和运行应用程序逻辑,为网页生成核心的动态内容。但它不太适合提供静态内容(例如图像和JavaScript文件),也不太适合在多台服务器之间进行负载均衡。
要充分发挥Node.js的作用,需要缓存静态内容,在多个应用服务器之间进行代理和负载均衡,并管理客户端、Node.js与辅助程序(例如运行Socket.IO的服务器)之间的端口争用。NGINX可用于实现所有这些功能,是优化Node.js性能的实用工具。
使用以下技巧提升Node.js应用性能:
注意:要快速提升Node.js应用性能,可以修改Node.js配置,以充分利用现代多核服务器。请参阅Node.js文档,了解如何让Node.js生成独立的子进程,数量与Web服务器上的CPU数量相同。随后,每个进程会自动分配到一个且仅一个CPU上,从而显著提升性能。
技巧 1:部署反向代理服务器
当我们看到应用服务器直接暴露于传入的互联网流量,并被用于高性能网站的核心时,NGINX, Inc.团队总会感到有些担忧。例如,许多基于WordPress的网站以及Node.js网站都是如此。
与大多数应用服务器相比,Node.js在设计上更注重可扩展性,其Web服务器端也能较好地处理大量互联网流量。但提供Web服务并非Node.js的核心用途,也不是它真正为之构建的功能。
如果网站流量较大,提升应用性能的第一步是在Node.js服务器前部署反向代理服务器。这样可以避免Node.js服务器直接暴露于互联网流量,并可灵活使用多台应用服务器、在服务器之间进行负载均衡以及缓存内容。

将NGINX作为反向代理服务器部署在现有服务器架构前端,并进一步用于其他场景,是NGIN的核心用例之一,全球已有数千万个网站采用这种方式。
将NGINX用作Node.js反向代理服务器具有以下特定优势:
- 简化权限处理和端口分配
- 更高效地提供静态图像(请参阅“下一条提示”)
- 有效管理Node.js崩溃
- 缓解DoS攻击
注意:这些教程介绍了如何在Ubuntu 14.04或CentOS环境中将NGINX用作反向代理服务器,并为需要在Node.js前端部署NGINX的用户提供实用概述。
技巧 2:缓存静态文件
随着基于Node.js的网站使用量增长,服务器将开始不堪重负。此时需要采取两项措施:
- 充分发挥Node.js服务器的性能
- 轻松添加应用服务器,并在这些服务器之间实现负载均衡
其实,这很容易实现。首先按照上一条提示中的说明,将NGINX部署为反向代理服务器。这样便于实现缓存、负载均衡(当有多个Node.js服务器时)等功能。
应用容器平台Modulus的网站上有一篇实用文章,介绍如何使用NGINX大幅提升Node.js应用性能。当所有工作都由Node.js独立处理时,作者的网站平均每秒可处理近900个请求。使用NGINX作为反向代理服务器来提供静态内容后,同一网站每秒可处理超过1,600个请求,性能提升近2倍。
性能提升一倍后,您将有更多时间采取其他措施来满足进一步增长的需求,例如审查并酌情改进网站设计、优化应用程序代码,以及部署更多应用程序服务器。
以下配置代码适用于在Modulus上运行的网站:
server { listen 80;
server_name static-test-47242.onmodulus.net;
root /mnt/app;
index index.html index.htm;
location /static/ {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://node-test-45750.onmodulus.net;
}
}NGINX, Inc.的Patrick Nommensen在这篇详尽的文章中介绍了如何缓存其个人博客中的静态内容。该博客基于Ghost开源博客平台运行,而Ghost是一款Node.js应用程序。虽然部分细节仅适用于Ghost,但其中的大部分代码也可复用于其他Node.js应用程序。
例如,在NGINX的location块中,您可能需要将某些内容排除在缓存范围之外。通常不应缓存博客平台的管理界面。以下配置代码可禁用Ghost管理界面的缓存(即将其排除在缓存范围之外):
location ~ ^/(?:ghost|signout) { proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://ghost_upstream;
add_header Cache-Control "no-cache, private, no-store,
must-revalidate, max-stale=0, post-check=0, pre-check=0";
}有关提供静态内容的一般信息,请参阅《NGINX Plus 管理指南》。该管理指南包含配置说明、针对文件查找成功或失败情况的多种响应选项,以及进一步提升性能的优化方法。
在NGINX服务器上缓存静态文件,可显著减轻Node.js应用服务器的工作负载,从而大幅提升其性能。
技巧 3:实现 Node.js 负载均衡器
要让Node.js应用实现高性能,也就是近乎不受限的性能,真正的关键在于运行多个应用服务器,并在所有服务器之间实现负载均衡。
Node.js负载均衡可能尤其棘手,因为Node.js支持在Web浏览器中运行的JavaScript代码与在Node.js应用服务器上运行的JavaScript代码进行高度交互,并以JSON对象作为数据交换媒介。这意味着特定客户端会话需要持续在特定应用服务器上运行,而使用多台应用服务器时,会话持久性本身就难以实现。
互联网和Web的一大优势是高度无状态,这意味着任何能够访问所请求文件的服务器都可以处理客户端请求。Node.js打破了这种无状态模式,更适合在有状态环境中运行,即由同一台服务器持续响应特定客户端的请求。
更适合满足此要求的是NGINX Plus,而不是NGINX Open Source。这两个NGINX版本非常相似,但一个主要区别在于它们支持不同的负载均衡算法。
NGINX支持无状态负载均衡方法:
- 轮询:新请求会发送到列表中的下一台服务器。
- 最少连接:新请求将发送到活动连接数最少的服务器。
- IP哈希:新请求将发送到根据客户端IP地址的哈希值分配的服务器。
在这些方法中,只有IP Hash能够可靠地将特定客户端的请求发送到同一台服务器,这对Node.js应用程序有利。但是,IP Hash很容易导致一台服务器收到的请求数量过多,而其他服务器收到的请求较少,正如这篇介绍负载均衡技术的博客文章所述。此方法支持有状态会话,但可能导致请求无法在服务器资源之间得到合理分配。
与NGINX不同,NGINX Plus支持会话持久性。启用会话持久性后,来自同一客户端的所有请求都会可靠地发送到同一台服务器,从而充分发挥Node.js在客户端与服务器之间进行有状态通信的优势,以及NGINX Plus的高级负载均衡能力。
因此,可以使用NGINX或NGINX Plus在多台Node.js服务器之间实现负载均衡。不过,只有使用NGINX Plus,才有望同时充分发挥负载均衡性能,并实现适合Node.js的有状态通信。NGINX Plus内置的应用健康检查和监控功能在此也很有用。
NGINX Plus还支持会话排空。应用服务器收到退出服务的请求后,可通过此功能正常完成当前会话。
技巧 4:代理 WebSocket 连接
所有版本的HTTP都采用“拉取”通信模式,由客户端向服务器请求文件。WebSocket则用于实现“推送”和“推送/拉取”通信,使服务器能够主动发送客户端尚未请求的文件。
The WebSocket 协议可更轻松地支持客户端与服务器之间更可靠的交互,同时减少数据传输量并尽可能降低延迟。需要时,可以建立全双工连接,使客户端和服务器均可按需发起和接收请求。
WebSocket协议提供功能强大的JavaScript接口,因此非常适合将Node.js用作应用服务器;对于事务量适中的Web应用,也适合将其用作Web服务器。当事务量增加时,可以在客户端与Node.js Web服务器之间部署NGINX,并使用NGINX或NGINX Plus来缓存静态文件,以及在多个应用服务器之间进行负载均衡。
Node.js通常与Socket.IO配合使用。Socket.IO是一种WebSocket API,在Node.js应用中已得到广泛使用。这可能导致端口80(用于HTTP)或端口443(用于HTTPS)变得非常拥挤,解决方法是将请求代理到Socket.IO服务器。可以使用NGINX作为代理服务器,如上所述,还可获得静态文件缓存、负载均衡等附加功能。

以下是server.js Node应用程序文件的代码。该应用程序侦听端口5000,充当代理服务器(而非Web服务器),并将请求路由到相应端口:
var io = require('socket.io').listen(5000);
io.sockets.on('connection', function (socket) {
socket.on('set nickname', function (name) {
socket.set('nickname', name, function () {
socket.emit('ready');
});
});
socket.on('msg', function () {
socket.get('nickname', function (err, name) {
console.log('Chat message by ', name);
});
});
});在index.html文件中添加以下代码,以连接服务器应用程序,并在该应用程序与用户浏览器之间建立WebSocket连接:
<script src="/socket.io/socket.io.js"></script><script>// <![CDATA[
var socket = io(); // your initialization code here.
// ]]>
</script>有关包括NGINX配置在内的完整说明,请参阅我们关于将NGINX和NGINX Plus与Node.js和Socket.IO配合使用的博客文章。如需深入了解此类Web应用可能面临的架构和基础设施问题,请参阅我们关于实时Web应用和WebSocket的博客文章。
技巧 5:实施 SSL/TLS 和 HTTP/2
越来越多的网站开始使用SSL/TLS来保护网站上的所有用户交互。是否以及何时采用SSL/TLS由您决定;如果决定采用,NGINX可通过以下两种方式支持这一过渡:
- 将NGINX设置为反向代理后,可以在NGINX中终止与客户端之间的SSL/TLS连接。Node.js服务器与NGINX反向代理服务器之间会双向发送和接收未加密的请求和内容。
- 初步迹象表明,使用新版HTTP协议HTTP/2,可能会在很大程度上甚至完全抵消使用SSL/TLS所带来的性能损失。NGINX支持HTTP/2,可同时终止HTTP/2和SSL/TLS连接,因此无需对Node.js应用服务器进行任何更改。
需要执行的实施步骤包括:更新Node.js配置文件中的URL,在NGINX配置中建立并优化安全连接,以及根据需要使用SPDY或HTTP/2。添加HTTP/2支持后,支持HTTP/2的浏览器版本将使用这一新协议与应用通信;较旧的浏览器版本则继续使用HTTP/1.x。

以下配置代码适用于使用SPDY的Ghost博客,具体说明请参见此处。其中包括OCSP装订等高级功能。有关使用NGINX终止SSL连接时的注意事项(包括OCSP装订选项),请参见此处。有关这些主题的概述,请参见此处。
只需稍作修改,即可配置Node.js应用,并可选择现在升级到HTTP/2,或在2016年初停止支持SPDY时再升级。
server { server_name domain.com;
listen 443 ssl spdy;
spdy_headers_comp 6;
spdy_keepalive_timeout 300;
keepalive_timeout 300;
ssl_certificate_key /etc/nginx/ssl/domain.key;
ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
ssl_buffer_size 1400;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/trust.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
add_header X-Cache $upstream_cache_status;
location / {
proxy_cache STATIC;
proxy_cache_valid 200 30m;
proxy_cache_valid 404 1m;
proxy_pass http://ghost_upstream;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_hide_header X-powered-by;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
expires 10m;
}
location /content/images {
alias /path/to/ghost/content/images;
access_log off;
expires max;
}
location /assets {
alias /path/to/ghost/themes/uno-master/assets;
access_log off;
expires max;
}
location /public {
alias /path/to/ghost/built/public;
access_log off;
expires max;
}
location /ghost/scripts {
alias /path/to/ghost/core/built/scripts;
access_log off;
expires max;
}
location ~ ^/(?:ghost|signout) {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://ghost_upstream;
add_header Cache-Control "no-cache, private, no-store,
must-revalidate, max-stale=0, post-check=0, pre-check=0";
proxy_set_header X-Forwarded-Proto https;
}
}
总结
本篇博文介绍了可用于提升Node.js应用性能的一些重要措施,重点说明如何在Node.js应用架构中加入NGINX,包括将NGINX用作反向代理服务器、缓存静态文件、实现负载均衡、代理WebSocket连接,以及终止SSL/TLS和HTTP/2协议。
NGINX与Node.js的组合被广泛认为可用于创建适合微服务的新应用,或为使用Java或Microsoft .NET的现有SOA应用增强灵活性和功能。本文将帮助您优化Node.js应用;如果您愿意,还可充分发挥Node.js与NGINX协同工作的优势。
关于作者
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

