为什么要做性能监控?
Node.js 应用在生产环境中可能遇到各种问题:内存泄漏、CPU 占用过高、响应变慢等。建立完善的监控体系是保障服务稳定性的关键。
监控指标
1. 系统指标
2. 应用指标
3. 业务指标
内存泄漏排查
1. 使用 heapdump
const heapdump = require('heapdump');
// 定期生成堆快照
setInterval(() => {
heapdump.writeSnapshot(`/tmp/${Date.now()}.heapsnapshot`);
}, 60 * 60 * 1000);2. 使用 Chrome DevTools
--inspect 参数chrome://inspect3. 常见内存泄漏场景
// 1. 全局变量
const cache = {}; // 无限增长
// 2. 闭包引用
function createHandler() {
const largeData = new Array(1000000);
return () => {
console.log(largeData.length); // largeData 永远不会被释放
};
}
// 3. 未清理的定时器
setInterval(() => {
// 一些操作
}, 1000); // 忘记 clearIntervalCPU Profiling
1. 使用 --prof 参数
node --prof app.js
# 生成 isolate-*.log 文件
node --prof-process isolate-*.log > processed.txt2. 使用 clinic
npm install -g clinic
clinic doctor -- node app.js
clinic flame -- node app.js3. 使用 0x
npm install -g 0x
0x app.js事件循环优化
1. 避免阻塞事件循环
// 不好的做法
app.get('/slow', (req, res) => {
const result = heavyComputation(); // 阻塞事件循环
res.json(result);
});
// 好的做法
app.get('/slow', async (req, res) => {
const result = await runInWorker(heavyComputation);
res.json(result);
});2. 使用 Worker Threads
const { Worker } = require('worker_threads');
function runInWorker(fn) {
return new Promise((resolve, reject) => {
const worker = new Worker(`
const { parentPort } = require('worker_threads');
const result = ${fn.toString()}();
parentPort.postMessage(result);
`, { eval: true });
worker.on('message', resolve);
worker.on('error', reject);
});
}3. 监控事件循环延迟
const { monitorEventLoopDelay } = require('perf_hooks');
const histogram = monitorEventLoopDelay({ resolution: 20 });
histogram.enable();
setInterval(() => {
console.log({
mean: histogram.mean / 1e6, // ms
p99: histogram.percentile(99) / 1e6,
});
}, 5000);监控工具
1. Prometheus + Grafana
const client = require('prom-client');
const register = new client.Registry();
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
});
register.registerMetric(httpRequestDuration);
// 在中间件中使用
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ method: req.method, route: req.path, status: res.statusCode });
});
next();
});2. APM 工具
总结
Node.js 性能监控与调优的关键:
记住,预防胜于治疗。在开发阶段就注意性能问题,比在生产环境中救火要好得多。