性能优化是应用上线后的持续工作。Node.js 应用可能会遇到 CPU 瓶颈、内存泄漏、响应缓慢等问题。本章将系统性地介绍从代码层面到架构层面的优化策略,帮助你提升 Node.js 应用的吞吐量和响应速度。
编写高效的 JavaScript 代码是性能优化的基础。
Node.js 的优势在于异步 I/O,确保所有 I/O 操作都使用异步版本,避免阻塞事件循环。例如使用 fs.promises 代替 fs.readFileSync。
// 不要这样做
const data = fs.readFileSync('file.txt'); // 阻塞
// 应该这样做
const data = await fs.promises.readFile('file.txt');
除了文件 I/O,还有其它同步 API(如 crypto.randomBytesSync),在服务器初始化之外应避免使用。
使用高效的算法和数据结构,避免不必要的计算。例如使用 for 循环代替 forEach 在性能敏感的场景,不过通常差异很小。
// 优化前
const result = arr.map(x => x * 2).filter(x => x > 10).reduce((acc, x) => acc + x, 0);
// 如果数组很大,考虑一次循环完成
let sum = 0;
for (const x of arr) {
const doubled = x * 2;
if (doubled > 10) sum += doubled;
}
原生方法如 JSON.parse、Array.prototype 方法通常比自定义实现更快。
全局变量、未清理的监听器、闭包引用等会导致内存泄漏。使用 WeakMap/WeakSet 处理缓存,及时移除事件监听器。
Node.js 单线程模型下,利用多核 CPU 需要集群模式。
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`工作进程 ${worker.process.pid} 退出,重启新进程`);
cluster.fork();
});
} else {
// 工作进程启动应用服务器
require('./app');
}
PM2 内置负载均衡,只需一行命令即可启动集群模式:
pm2 start app.js -i max
Nginx 位于 Node.js 之前,可以处理静态文件、负载均衡和 SSL 终止。配置示例:
upstream node_app {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
location / {
proxy_pass http://node_app;
}
}
内存泄漏会导致性能下降和进程崩溃。定期监控内存使用。
使用 heapdump 或 v8-profiler 生成堆快照,在 Chrome DevTools 中分析。
const heapdump = require('heapdump');
heapdump.writeSnapshot((err, filename) => {
console.log('堆快照已写入', filename);
});
emitter.once 或手动移除。setInterval 和 setTimeout。可以通过 Node.js 命令行参数调整 GC 行为,例如:
node --max-old-space-size=4096 app.js # 限制老生代内存为 4GB
数据库往往是瓶颈所在。
确保查询字段有索引。MongoDB:
db.users.createIndex({ email: 1 });
数据库客户端默认有连接池,但需根据负载调整大小。例如 MySQL:
const pool = mysql.createPool({
connectionLimit: 10,
// ...
});
只返回需要的字段,使用分页,避免 N+1 查询。使用 ORM 时注意 eager loading。
对于读多写少的场景,可以使用主从复制,将读请求分发到从库。
缓存可以大幅减少数据库和后端计算的压力。
使用 Node.js 的内存缓存(如 node-cache)存储频繁访问的数据。注意内存限制。
const NodeCache = require('node-cache');
const myCache = new NodeCache({ stdTTL: 600 }); // 缓存10分钟
function getUser(id) {
let user = myCache.get(id);
if (user) return Promise.resolve(user);
return db.getUser(id).then(user => {
myCache.set(id, user);
return user;
});
}
Redis 是更强大的缓存解决方案,支持持久化和数据结构操作。
const redis = require('redis');
const client = redis.createClient();
async function getCachedData(key) {
const data = await client.get(key);
if (data) return JSON.parse(data);
const fresh = await fetchData();
await client.setEx(key, 3600, JSON.stringify(fresh));
return fresh;
}
设置正确的 HTTP 头(Cache-Control、ETag),让浏览器或 CDN 缓存响应。
res.setHeader('Cache-Control', 'public, max-age=3600');
使用 compression 中间件压缩响应体:
const compression = require('compression');
app.use(compression());
Node.js 支持 HTTP/2,可以减少连接开销。需配合 HTTPS 使用。
将静态文件交给 Nginx 或 CDN 处理,而不是 Node.js。
没有测量就没有优化。使用工具监控应用性能。
process.cpuUsage()、process.memoryUsage()。pm2 monit 查看 CPU/内存。morgan 记录请求耗时。const morgan = require('morgan');
morgan.token('response-time-ms', (req, res) => res.getHeader('X-Response-Time-ms'));
app.use(morgan(':method :url :status :response-time-ms ms'));
node_modules,使用 npm prune。npm ci 而非 npm install 在 CI 中,速度更快且严格。NODE_ENV=production:许多库(如 Express)会启用生产优化。max-old-space-size。