博客 | NGINX

Node.jsapplications的 5 个性能技巧

NGINX-F5-horiz-black-type-RGB 的一部分
Floyd Smith 缩略图
弗洛伊德·史密斯
2015 年 11 月 16 日发布
如果#nginx 没有位于你的节点服务器前面,那么你可能做错了。
Bryan Hughes在 Twitter 上

Node.js 是用世界上最流行的编程语言 JavaScript 创建服务器应用的领先工具。 Node.js 同时提供 Web 服务器和应用服务器的功能,现在被认为是各种基于微服务的开发和交付的关键工具。 (下载有关 Node.js 和 NGINX 的免费 Forrester 报告。)

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应用的性能:

  1. 实现反向代理服务器
  2. 缓存静态文件
  3. 在多个服务器之间平衡流量负载
  4. 代理 WebSocket 连接
  5. 实施 SSL/TLS 和 HTTP/2

笔记: 提高 Node.js应用性能的快速方法是修改 Node.js 配置以利用现代多核服务器。 查看Node.js 文档来了解如何让 Node.js 生成单独的子进程 – 数量等于 Web 服务器上的 CPU 数量。 然后每个进程都会神奇地在一个且仅一个 CPU 上找到自己的位置,从而大大提高性能。

提示 1 - 实现反向代理服务器

当我们看到作为高性能网站核心的应用服务器直接暴露在传入的互联网流量中时,NGINX, Inc. 的员工总是感到有点震惊。 例如,这包括许多基于 WordPress 的网站以及 Node.js 网站。

Node.js 与大多数应用服务器相比,在更大程度上是为可扩展性而设计的,并且其 Web 服务器端可以相当好地处理大量的互联网流量。 但 Web 服务并不是 Node.js 存在的理由 —— 它真正的设计目的并不是提供 Web 服务。

如果您有一个高流量网站,提高应用性能的第一步是在 Node.js 服务器前面放置一个反向代理服务器。 这可以保护 Node.js 服务器免受互联网流量的直接影响,并允许您在使用多个应用服务器、跨服务器负载均衡和缓存内容方面具有很大的灵活性。

将 NGINX 放在现有服务器设置前面作为反向代理服务器,然后附加其他用途,是 NGINX 的一个核心用例,全球数千万个网站均已实现该用例。

使用 NGINX 作为 Node.js 反向代理服务器有以下具体优势,包括:

  • 简化权限处理和端口分配
  • 更有效地提供静态图像(参见下一条提示
  • 成功管理 Node.js 崩溃
  • 缓解 DoS 攻击

笔记: 这些教程解释了如何在Ubuntu 14.04CentOS环境中使用 NGINX 作为反向代理服务器,对于将 NGINX 放在 Node.js 前面的任何人来说,它们都是有用的概述。

技巧 2 – 缓存静态文件

随着基于 Node.js 的网站使用量的增长,服务器将开始显现压力。 此时您要做两件事:

  1. 充分利用 Node.js 服务器
  2. 轻松添加应用服务器并在它们之间实现负载平衡

这其实很容易做到。 首先将 NGINX 实现为反向代理服务器,如上一个技巧中所述。 这使得实现缓存、负载均衡(当您有多个 Node.js 服务器时)等变得容易。

应用容器平台 Modulus 的网站上有一篇关于使用 NGINX 增强 Node.js应用性能的有用文章。由于 Node.js 可以自行完成所有工作,因此作者的网站平均每秒能够处理近 900 个请求。 使用 NGINX 作为反向代理服务器,提供静态内容,同一站点每秒可处理超过 1600 个请求 - 性能提高了近 2 倍。

性能翻倍可以让您有时间采取其他措施来适应进一步的增长,例如审查(并可能改进)您的网站设计、优化您的应用代码以及部署其他应用服务器。

以下是适用于在 Modulus 上运行的网站的配置代码:

服务器 { 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 开源博客平台(一个 Node.js应用)上。 尽管某些细节是 Ghost 特有的,但您可以将大部分代码重用于其他 Node.js应用。

例如,在 NGINX位置块中,您可能希望免除某些内容的缓存。 例如,您通常不想缓存博客平台的管理界面。 以下是禁用(或免除)Ghost 管理界面缓存的配置代码:

位置 ~ ^/(?: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 对象作为数据交换的媒介。 这意味着给定的客户端会话在特定的应用服务器上持续运行,而会话持久性在多个应用服务器上本质上很难实现。

互联网和网络的主要优势之一是其高度的无国籍性,其中包括任何有权访问请求文件的服务器都可以满足客户端的请求。 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 是一种实现“推送”和“推送/拉取”通信的工具,其中服务器可以主动发送客户端未请求的文件。

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 用作代理服务器,如上所述,还可以获得其他功能,如静态文件缓存、负载均衡等。

以下是监听端口 5000 的 server.js 节点应用文件的代码。 它充当代理服务器(而不是 Web 服务器)并将请求路由到正确的端口:

var io = require('socket.io').listen(5000); 
io.sockets.on('connection', function (socket) {
socket.on('设置昵称', function (name) {
socket.set('昵称', name, function () {
socket.emit('ready');
});
});

socket.on('msg', function () {
socket.get('昵称', function (err, name) {
console.log('聊天消息来自 ', name);
});
});
});

在您的index.html文件中,添加以下代码以连接到您的服务器应用并在应用和用户浏览器之间实例化 WebSocket:

<script src="/socket.io/socket.io.js"></script><script>// <![CDATA[
var socket = io(); // 您的初始化代码在此。
// ]]>
</script>

有关完整说明(包括 NGINX 配置),请参阅有关使用 NGINX 和 NGINX Plus与 Node.js 和 Socket.IO 的博客文章。 要详细了解此类 Web应用的潜在架构和基础设施问题,请参阅我们关于实时 Web应用和 WebSocket 的博客文章。

提示 5 – 实施 SSL/TLS 和 HTTP/2

越来越多的网站使用 SSL/TLS 来保护网站上的所有用户交互。 是否以及何时进行此举由您决定,但是如果您这样做,NGINX 将通过两种方式支持转换:

  1. 一旦将 NGINX 设置为反向代理,您就可以终止与 NGINX 中客户端的 SSL/TLS 连接。 Node.js 服务器与 NGINX 反向代理服务器来回发送和接收未加密的请求和内容。
  2. 早期迹象表明,使用新版本的 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应用并从 SPDY 升级到 HTTP/2,现在或当 SPDY 支持在 2016 年初消失时即可。

服务器 { 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;
解析器 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 主机 $http_host;
10 分钟后过期;
}

location /content/images {
别名 /path/to/ghost/content/images;
access_log off;
最大过期时间;
}

location /assets {
别名 /path/to/ghost/themes/uno-master/assets;
access_log off;
最大过期时间;
}

location /public {
别名 /path/to/ghost/built/public;
access_log off;
最大过期时间;
}

location /ghost/scripts {
别名 /path/to/ghost/core/built/scripts;
access_log off;
最大过期时间;
}

location ~ ^/(?:ghost|signout) { 
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header 主机 $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应用中进行的一些最重要的性能改进。 它专注于将 NGINX 与 Node.js 一起添加到您的应用组合中 - 通过使用 NGINX 作为反向代理服务器,缓存静态文件、进行负载均衡、代理 WebSocket 连接以及终止 SSL/TLS 和 HTTP/2 协议。

NGINX 和 Node.js 的结合被广泛认为是创建新的微服务友好型应用或为使用 Java 或 Microsoft .NET 的现有基于 SOA 的应用增加灵活性和功能的一种方式。 这篇文章可以帮助您优化 Node.js应用,并且如果您愿意的话,还可以让 Node.js 和 NGINX 之间的合作变得生动活泼。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”