Node.js 性能优化:10个关键技巧与实践

作者:缓存笔记

时间:2026-10-28

浏览:589

本文总结了提升 Node.js 应用性能的十个关键技巧。核心策略包括避免同步阻塞操作、合理配置 Socket 池以处理高并发、将静态资源交由 Nginx 或 CDN 处理,以及采用客户端渲染减少服务器负载。此外,通过启用 Gzip 压缩、并行化阻塞操作、实现无状态 Session、使用二进制模块及标准 V8 函数,并保持代码精简,可显著降低 CPU 和 I/O 开销,提升系统响应速度。

文章目录

Node.js 性能优化概述

以下是我们在 Node.js 开发中遵循的十条性能优化准则:

避免同步代码

1. 避免使用同步代码

Node.js 在架构上采用单线程模型。为了支持高并发请求,必须确保线程不会因等待阻塞、同步或耗时操作而被占用。Node.js 的核心设计理念自下而上均围绕异步机制构建,这使其成为事件驱动型程序的理想选择。

然而,同步或阻塞调用仍可能发生。例如,许多文件系统操作同时提供同步和异步版本,如 writeFile 和 writeFileSync。即使开发者有意控制同步方法,也可能无意中调用到阻塞性的第三方库函数。这种疏忽会对性能造成巨大影响。


// Good: write files asynchronously
fs.writeFile('message.txt', 'Hello Node', function (err) {
 console.log("It's saved and the server remains responsive!");
});
 
// BAD: write files synchronously
fs.writeFileSync('message.txt', 'Hello Node');
console.log("It's saved, but you just blocked ALL requests!");

我们在初始化日志记录时,曾无意间包含了一个将内容写入磁盘的同步调用。若未进行性能测试,此类问题极易被忽略。在基于 Node.js 实例的标准测试中,该同步调用导致系统性能从每秒处理数千次请求骤降至仅几十次。

关闭套接字池

2. 关闭套接字池

Node.js 的 HTTP 客户端默认启用套接字池,限制每台主机最多保留 5 个套接字。虽然复用套接字有助于控制资源增长,但在需要处理来自同一主机的大量并发请求时,这会成为瓶颈。此时,增大 maxSockets 值或直接关闭套接字池是更优的选择。


// Disable socket pooling
 
var http = require('http');
var options = {.....};
options.agent = false;
var req = http.request(options)

静态资源处理与客户端渲染

3. 不要让静态资源使用 Node.js

对于 CSS、图片等静态资源,应使用标准 Web 服务器(如 Nginx)而非 Node.js 进行处理。例如,LinkedIn 移动端即采用 Nginx。同时,利用内容分发网络(CDN)将全球静态资源缓存至边缘服务器,具有双重优势:一是降低 Node.js 服务器的负载,二是通过就近分发减少用户等待时间。

4. 在客户端渲染

简要对比服务器端渲染与客户端渲染。若使用 Node.js 在服务器端渲染,每次请求都会返回如下 HTML 页面:


<!-- An example of a simple webpage rendered entirely server side -->
 
<!DOCTYPE html>
<html>
 <head>
  <title>LinkedIn Mobile</title>
 </head>
 <body>
  <div class="header">
   <img src="http://mobile-cdn.linkedin.com/images/linkedin.png" alt="LinkedIn"/>
  </div>
  <div class="body">
   Hello John!
  </div>
 </body>
</html>

注意观察,除用户名外,页面其余部分均为静态内容。对于所有用户和页面重载,这部分内容完全一致。因此,更高效的策略是让 Node.js 仅以 JSON 格式返回页面所需的动态数据。

{"name": "John"} 页面的其余部分——所有静态 HTML 标记——可置于 JavaScript 模板中(如 underscore.js 模板):


<!-- An example of a JavaScript template that can be rendered client side -->
 
<!DOCTYPE html>
<html>
 <head>
  <title>LinkedIn Mobile</title>
 </head>
 <body>
  <div class="header">
   <img src="http://mobile-cdn.linkedin.com/images/linkedin.png" alt="LinkedIn"/>
  </div>
  <div class="body">
   Hello <%= name %>!
  </div>
 </body>
</html>

性能提升源于以下几点:如第 3 点所述,静态 JavaScript 模板可由 Web 服务器(如 Nginx)提供,或通过 CDN 分发。此外,JavaScript 模板可缓存于浏览器或本地存储。初始页面加载后,客户端仅需接收 JSON 数据,这将极大降低 CPU、I/O 及 Node.js 的负载。

启用 Gzip 压缩

5. 使用 gzip

Node.js 性能优化概述

许多服务器和客户端支持 gzip 压缩请求与响应。无论向远程服务器发送请求还是响应客户端,都应充分利用 gzip 压缩功能。

阻塞操作并行化

6. 并行化

Node.js 性能优化概述

尝试将所有阻塞操作——包括远程服务请求、数据库调用和文件系统访问——并行执行。这能减少最慢阻塞操作的等待时间,而非所有阻塞操作时间的总和。为保持回调和错误处理的简洁,我们使用 Step 库来控制流程。

Session 无状态化

7. Session 自由化

LinkedIn 移动端使用 Express 框架管理请求/响应周期。许多 Express 示例包含如下配置:

app.use(express.session({ secret: "keyboard cat" })); 默认情况下,Session 数据存储在内存中,这会随用户量增长给服务器带来巨大开销。虽然可使用 MongoDB 或 Redis 等外部存储,但每个请求都会产生远程调用 Session 数据的开销。最佳方案是在服务器端存储所有无状态数据。通过移除上述 Express 配置实现 Session 自由化,可获得更好的性能。

使用二进制模块与标准 V8 函数

8. 使用二进制模块

若可行,请使用二进制模块替代 JavaScript 模块。例如,当我们将 SHA 算法从纯 JavaScript 实现迁移至 Node.js 的编译版本时,性能得到了显著提升:


// Use built in or binary modules
var crypto = require('crypto');
var hash = crypto.createHmac("sha1",key).update(signatureBase).digest("base64");

9. 用标准的 V8 JavaScript 取代客户端库

许多JavaScript库都是为了在web浏览器上使用而创建的,因为在JavaScript环境不同时:比如,一些浏览器支持forEach,map和reduce这样的函数,但有些浏览器不支持。因此客户端库通常用许多低效的代码来克服浏览器的差异。另一方面,在Node.js中,你能确切地知道哪些JavaScript方法是有效的:V8 JavaScript引擎支撑Node.js实现ECMA-262第五版中指定的ECMAScript。直接用标准的V8 JavaScript函数替代客户端库,你会发现性能得到显著的提高。

保持代码精简

10. 让你的代码保持小且轻

移动设备访问速度慢且延迟高,这提示我们代码应保持精简。服务器端代码亦应遵循此理念。定期审视决策,自问:“我们真的需要这个模块吗?”“使用此框架的开销是否值得?”“能否用更简便的方法实现?”精简高效的代码通常运行更快、效率更高。

试试看

我们致力于提升移动应用的速度。建议在 iPhone 应用、Android 应用及 HTML5 移动版上进行测试,以评估我们的优化效果。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章