Node.js 性能监控与优化
Node.js 应用的性能指标、监控方案和优化策略
问题
如何对 Node.js 应用进行性能监控和优化?
解答
性能指标
Node.js 性能主要关注以下四个方面:
CPU
- CPU 负载:某个时间段内,占用以及等待 CPU 的进程总数
- CPU 使用率:CPU 时间占用状况,等于 1 - 空闲 CPU 时间 / CPU 总时间
Node 应用通常不会消耗很多 CPU,如果 CPU 占用率高,说明存在大量同步操作阻塞了异步任务回调。
内存
通过 process.memoryUsage() 获取内存使用情况:
const os = require('os');
// 获取当前 Node 内存堆栈情况
const { rss, heapUsed, heapTotal } = process.memoryUsage();
// 获取系统空闲内存
const sysFree = os.freemem();
// 获取系统总内存
const sysTotal = os.totalmem();
rss:Node 进程占用的内存总量heapTotal:堆内存的总量heapUsed:实际堆内存的使用量external:外部程序的内存使用量,包含 Node 核心 C++ 程序的内存使用量
Node 进程的最大内存容量为 1.5GB,需要注意避免内存泄露。
磁盘 I/O
硬盘 I/O 开销很大,花费的 CPU 时钟周期是内存的 164000 倍。可以使用 Redis、Memcached 等内存缓存来优化高频访问、生成代价高的数据。
网络
网络性能影响服务响应速度和吞吐量。
性能监控
使用 Easy-Monitor 2.0 进行性能监控,这是一个轻量级的 Node.js 内核性能监控工具。
在项目入口文件中引入:
const easyMonitor = require('easy-monitor');
easyMonitor('你的项目名称');
启动项目后,访问 http://localhost:12333 即可查看监控界面。
性能优化
使用最新版本 Node.js
新版本带来 V8 引擎更新和 Node.js 内部代码优化,性能提升明显。
正确使用 Stream
对于大文件,使用流式传输而不是一次性读入内存:
const http = require('http');
const fs = require('fs');
// 不推荐:一次性读入内存
http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
});
});
// 推荐:使用流
http.createServer(function (req, res) {
fs.createReadStream(__dirname + '/data.txt').pipe(res);
});
代码层面优化
合并数据库查询,减少查询次数:
// 不推荐:多次查询
for (let user_id of userIds) {
let account = await user_account.findOne(user_id);
}
// 推荐:批量查询
const user_account_map = {};
const accounts = await user_account.find({ user_id: { $in: userIds } });
accounts.forEach(account => {
user_account_map[account.user_id] = account;
});
for (let user_id of userIds) {
let account = user_account_map[user_id];
}
内存管理优化
V8 将内存分为新生代和老生代。避免内存泄露,减少不必要的内存占用:
// 不推荐:造成内存泄露
const leak = [];
const buffer = fs.readFileSync(__dirname + '/source/index.htm');
app.use(mount('/', async (ctx) => {
ctx.status = 200;
ctx.type = 'html';
ctx.body = buffer;
leak.push(fs.readFileSync(__dirname + '/source/index.htm')); // 内存泄露
}));
// 推荐:复用 buffer
const buffer = fs.readFileSync(__dirname + '/source/index.htm');
app.use(mount('/', async (ctx) => {
ctx.status = 200;
ctx.type = 'html';
ctx.body = buffer;
}));
使用对象池机制,复用频繁创建和销毁的对象,减少内存抖动,提升性能。
关键点
- 监控 CPU、内存、I/O、网络四个核心指标,Node 进程最大内存为 1.5GB
- 使用 Easy-Monitor 等工具进行实时性能监控
- 大文件传输使用 Stream 而不是一次性读入内存
- 合并数据库查询,使用内存缓存(Redis/Memcached)优化高频访问
- 避免内存泄露,使用对象池复用频繁创建的对象
目录