Node.js 性能优化概述
本文档全面介绍 Node.js 应用的性能优化方法论,涵盖性能指标定义、测试方法、分析工具、优化策略及最佳实践。
系统架构概述
Node.js 性能优化体系架构
code
┌─────────────────────────────────────────────────────────────────┐
│ 性能优化体系架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 性能监控层 │───▶│ 性能分析层 │───▶│ 性能优化层 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ • 指标采集 │ │ • 瓶颈定位 │ │ • 代码优化 │ │
│ │ • 日志记录 │ │ • 根因分析 │ │ • 架构优化 │ │
│ │ • 告警触发 │ │ • 趋势分析 │ │ • 资源优化 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘核心模块说明
| 模块名称 | 功能描述 | 关键技术 |
|---|---|---|
| 性能监控层 | 实时采集应用性能数据 | Prometheus, Grafana, APM |
| 性能分析层 | 分析性能瓶颈和根因 | Chrome DevTools, clinic.js |
| 性能优化层 | 实施优化策略 | 缓存、集群、异步处理 |
性能指标
响应时间指标
响应时间是衡量系统性能的最直接指标,反映用户体验质量。
指标定义
| 指标名称 | 定义说明 | 计算公式 | 优秀阈值 |
|---|---|---|---|
| 平均响应时间 (ART) | 所有请求响应时间的平均值 | ΣRT / n | < 100ms |
| 最大响应时间 (Max RT) | 请求响应时间的最大值 | max(RT₁...RTₙ) | < 1000ms |
| P95 响应时间 | 95% 的请求响应时间低于此值 | percentile(RT, 95) | < 200ms |
| P99 响应时间 | 99% 的请求响应时间低于此值 | percentile(RT, 99) | < 500ms |
响应时间分布分析
javascript
const { Histogram } = require('prom-client');
// 创建响应时间直方图
const responseTimeHistogram = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP 请求响应时间分布',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 1, 3, 5, 10] // 秒
});
// 中间件记录响应时间
function responseTimeMiddleware(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
responseTimeHistogram
.labels(req.method, req.route?.path || req.path, res.statusCode)
.observe(duration);
});
next();
}吞吐量指标
吞吐量反映系统的处理能力,是容量规划的关键依据。
指标详解
| 指标名称 | 英文全称 | 定义说明 | 典型值范围 |
|---|---|---|---|
| QPS | Queries Per Second | 每秒查询数,适用于读操作 | 1000-10000+ |
| TPS | Transactions Per Second | 每秒事务数,适用于写操作 | 500-5000+ |
| 并发连接数 | Concurrent Connections | 同时活跃的连接数 | 取决于系统配置 |
吞吐量监控实现
javascript
const counter = require('prom-client').Counter;
// 请求计数器
const requestCounter = new counter({
name: 'http_requests_total',
help: 'HTTP 请求总数',
labelNames: ['method', 'route', 'status_code']
});
// 计算实时 QPS
class QPSCalculator {
constructor(windowSize = 60) {
this.requests = [];
this.windowSize = windowSize * 1000; // 转换为毫秒
}
record() {
this.requests.push(Date.now());
this.cleanOldRecords();
}
cleanOldRecords() {
const threshold = Date.now() - this.windowSize;
this.requests = this.requests.filter(time => time > threshold);
}
getQPS() {
this.cleanOldRecords();
return this.requests.length / (this.windowSize / 1000);
}
}
const qpsCalculator = new QPSCalculator();资源使用指标
内存指标详解
javascript
/**
* 获取详细的内存使用情况
* @returns {Object} 内存指标对象
*/
function getMemoryMetrics() {
const used = process.memoryUsage();
return {
// 常驻内存集大小(包含所有 C++ 对象和 JavaScript 对象)
rss: {
value: used.rss,
formatted: formatBytes(used.rss),
description: '进程分配的总物理内存'
},
// V8 堆内存总量
heapTotal: {
value: used.heapTotal,
formatted: formatBytes(used.heapTotal),
description: 'V8 分配的堆内存总量'
},
// V8 堆内存使用量
heapUsed: {
value: used.heapUsed,
formatted: formatBytes(used.heapUsed),
description: 'V8 堆内存实际使用量',
usagePercent: ((used.heapUsed / used.heapTotal) * 100).toFixed(2) + '%'
},
// 外部内存使用(C++ 对象)
external: {
value: used.external,
formatted: formatBytes(used.external),
description: '绑定到 V8 管理的 JavaScript 对象的 C++ 对象内存'
},
// ArrayBuffer 内存
arrayBuffers: {
value: used.arrayBuffers || 0,
formatted: formatBytes(used.arrayBuffers || 0),
description: 'ArrayBuffer 和 SharedArrayBuffer 的内存'
}
};
}
function formatBytes(bytes) {
const units = ['B', 'KB', 'MB', 'GB'];
let unitIndex = 0;
let value = bytes;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex++;
}
return `${value.toFixed(2)} ${units[unitIndex]}`;
}CPU 使用监控
javascript
const os = require('os');
/**
* 获取 CPU 使用率
* @param {number} interval - 采样间隔(毫秒)
* @returns {Promise<number>} CPU 使用率百分比
*/
async function getCPUUsage(interval = 100) {
return new Promise((resolve) => {
const startUsage = process.cpuUsage();
const startTime = process.hrtime.bigint();
setTimeout(() => {
const elapsedTime = Number(process.hrtime.bigint() - startTime) / 1e6; // 毫秒
const elapsedUsage = process.cpuUsage(startUsage);
const totalUsage = elapsedUsage.user + elapsedUsage.system;
const cpuPercent = (totalUsage / 1000 / elapsedTime) * 100;
resolve(cpuPercent.toFixed(2));
}, interval);
});
}
// 系统负载监控
function getSystemLoad() {
const loadAvg = os.loadavg();
const cpuCount = os.cpus().length;
return {
load1: loadAvg[0].toFixed(2),
load5: loadAvg[1].toFixed(2),
load15: loadAvg[2].toFixed(2),
cpuCount,
loadStatus: loadAvg[0] < cpuCount ? '正常' : '过载'
};
}网络与磁盘 I/O 监控
javascript
const networkMonitor = {
// 请求统计
stats: {
total: 0,
success: 0,
failed: 0,
totalBytes: 0
},
recordRequest(bytes, success = true) {
this.stats.total++;
if (success) {
this.stats.success++;
} else {
this.stats.failed++;
}
this.stats.totalBytes += bytes;
},
getNetworkMetrics() {
return {
...this.stats,
successRate: ((this.stats.success / this.stats.total) * 100).toFixed(2) + '%',
failureRate: ((this.stats.failed / this.stats.total) * 100).toFixed(2) + '%',
avgBytesPerRequest: Math.round(this.stats.totalBytes / this.stats.total)
};
}
};错误率指标
javascript
// 错误率监控类
class ErrorRateMonitor {
constructor(windowSize = 60) {
this.requests = [];
this.windowSize = windowSize * 1000;
}
record(success) {
this.requests.push({
timestamp: Date.now(),
success
});
this.cleanOldRecords();
}
cleanOldRecords() {
const threshold = Date.now() - this.windowSize;
this.requests = this.requests.filter(r => r.timestamp > threshold);
}
getMetrics() {
this.cleanOldRecords();
const total = this.requests.length;
const errors = this.requests.filter(r => !r.success).length;
return {
total,
errors,
errorRate: total > 0 ? ((errors / total) * 100).toFixed(2) + '%' : '0%',
status: this.getStatus(errors / total)
};
}
getStatus(errorRate) {
if (errorRate < 0.01) return '优秀';
if (errorRate < 0.05) return '良好';
if (errorRate < 0.1) return '警告';
return '严重';
}
}性能测试
压力测试工具对比
| 工具名称 | 特点 | 适用场景 | 安装方式 |
|---|---|---|---|
| Apache Bench (ab) | 简单易用,单线程 | 快速基准测试 | apt install apache2-utils |
| wrk | 多线程,高性能 | 高并发场景测试 | brew install wrk |
| autocannon | Node.js 原生,功能丰富 | Node.js 应用测试 | npm install -g autocannon |
| k6 | 脚本化,支持 CI/CD | 自动化测试流程 | brew install k6 |
| Artillery | YAML 配置,功能完整 | 复杂场景测试 | npm install -g artillery |
Apache Bench 使用详解
bash
# 基础测试:100 个请求,并发 10
ab -n 100 -c 10 http://localhost:3000/
# 带超时设置的测试
ab -n 1000 -c 100 -t 30 http://localhost:3000/api/users
# POST 请求测试
ab -n 100 -c 10 -p data.json -T application/json http://localhost:3000/api/create
# 输出关键指标说明:
# - Requests per second: QPS(每秒请求数)
# - Time per request: 平均响应时间
# - Failed requests: 失败请求数
# - 50%/95%/99%: 响应时间百分位数wrk 高级用法
bash
# 基础测试:12 线程,400 连接,持续 30 秒
wrk -t12 -c400 -d30s http://localhost:3000/
# 带延迟统计
wrk -t12 -c400 -d30s --latency http://localhost:3000/
# 使用 Lua 脚本自定义请求
wrk -t4 -c100 -d30s -s post.lua http://localhost:3000/api
# post.lua 内容示例
--[[
wrk.method = "POST"
wrk.body = '{"name":"test","value":123}'
wrk.headers["Content-Type"] = "application/json"
--]]autocannon 详细示例
bash
# 基础测试
autocannon -c 100 -d 5 http://localhost:3000/
# 多连接测试
autocannon -c 100 -w 10 -d 10 http://localhost:3000/
# 使用配置文件
autocannon -c config.jsonconfig.json 配置文件示例:
json
{
"url": "http://localhost:3000",
"connections": 100,
"duration": 10,
"pipelining": 10,
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer token123"
},
"requests": [
{
"method": "GET",
"path": "/api/users"
},
{
"method": "POST",
"path": "/api/users",
"body": "{\"name\":\"test\"}"
}
]
}K6 脚本化测试
javascript
// k6-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// 自定义指标
const errorRate = new Rate('errors');
const apiLatency = new Trend('api_latency');
export const options = {
stages: [
{ duration: '30s', target: 20 }, // 预热阶段
{ duration: '1m', target: 100 }, // 正常负载
{ duration: '30s', target: 200 }, // 峰值测试
{ duration: '30s', target: 0 }, // 恢复阶段
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% 请求 < 500ms
errors: ['rate<0.1'], // 错误率 < 10%
},
};
export default function () {
const res = http.get('http://localhost:3000/api/users');
check(res, {
'状态码为 200': (r) => r.status === 200,
'响应时间 < 500ms': (r) => r.timings.duration < 500,
'有返回数据': (r) => r.json().length > 0,
});
errorRate.add(res.status !== 200);
apiLatency.add(res.timings.duration);
sleep(1);
}运行命令:
bash
# 运行测试
k6 run k6-test.js
# 输出到 InfluxDB
k6 run --out influxdb=http://localhost:8086/k6 k6-test.js性能监控中间件
javascript
/**
* 性能监控中间件
* 提供完整的请求性能追踪功能
*/
class PerformanceMonitor {
constructor(options = {}) {
this.enabled = options.enabled ?? true;
this.logInterval = options.logInterval ?? 60000; // 默认 1 分钟
this.metrics = this.initMetrics();
if (this.enabled) {
this.startLogging();
}
}
initMetrics() {
return {
requests: {
total: 0,
success: 0,
errors: 0,
byStatus: {},
byPath: {}
},
responseTime: {
values: [],
max: 0,
min: Infinity
},
memory: {
samples: []
}
};
}
/**
* Express 中间件
*/
middleware() {
return (req, res, next) => {
if (!this.enabled) return next();
const start = process.hrtime.bigint();
const path = req.route?.path || req.path;
// 记录响应
res.on('finish', () => {
const duration = Number(process.hrtime.bigint() - start) / 1e6; // 毫秒
this.recordRequest({
path,
method: req.method,
statusCode: res.statusCode,
duration
});
});
next();
};
}
recordRequest(data) {
this.metrics.requests.total++;
// 按状态码统计
const status = data.statusCode;
this.metrics.requests.byStatus[status] =
(this.metrics.requests.byStatus[status] || 0) + 1;
// 按路径统计
this.metrics.requests.byPath[data.path] =
this.metrics.requests.byPath[data.path] || { count: 0, totalTime: 0 };
this.metrics.requests.byPath[data.path].count++;
this.metrics.requests.byPath[data.path].totalTime += data.duration;
// 响应时间统计
if (data.duration > this.metrics.responseTime.max) {
this.metrics.responseTime.max = data.duration;
}
if (data.duration < this.metrics.responseTime.min) {
this.metrics.responseTime.min = data.duration;
}
// 成功/失败统计
if (status >= 200 && status < 400) {
this.metrics.requests.success++;
} else {
this.metrics.requests.errors++;
}
}
startLogging() {
setInterval(() => {
const memory = process.memoryUsage();
this.metrics.memory.samples.push({
timestamp: Date.now(),
heapUsed: memory.heapUsed,
rss: memory.rss
});
// 只保留最近 100 个样本
if (this.metrics.memory.samples.length > 100) {
this.metrics.memory.samples.shift();
}
this.logMetrics();
}, this.logInterval);
}
logMetrics() {
const stats = this.getStats();
console.log('\n========== 性能监控报告 ==========');
console.log(`总请求数: ${stats.requests.total}`);
console.log(`成功率: ${stats.requests.successRate}`);
console.log(`平均响应时间: ${stats.responseTime.avg.toFixed(2)}ms`);
console.log(`最大响应时间: ${stats.responseTime.max.toFixed(2)}ms`);
console.log(`内存使用: ${formatBytes(stats.memory.heapUsed)}`);
console.log('==================================\n');
}
getStats() {
const req = this.metrics.requests;
const rt = this.metrics.responseTime;
return {
requests: {
total: req.total,
success: req.success,
errors: req.errors,
successRate: req.total > 0
? ((req.success / req.total) * 100).toFixed(2) + '%'
: '0%',
byStatus: req.byStatus,
byPath: Object.entries(req.byPath).map(([path, data]) => ({
path,
count: data.count,
avgTime: (data.totalTime / data.count).toFixed(2) + 'ms'
}))
},
responseTime: {
max: rt.max,
min: rt.min === Infinity ? 0 : rt.min,
avg: rt.values.length > 0
? rt.values.reduce((a, b) => a + b, 0) / rt.values.length
: 0
},
memory: process.memoryUsage()
};
}
/**
* 重置指标
*/
reset() {
this.metrics = this.initMetrics();
}
}
// 使用示例
const monitor = new PerformanceMonitor({
enabled: true,
logInterval: 30000
});
app.use(monitor.middleware());性能分析工具
Node.js 内置分析工具
--prof 标志性能分析
bash
# 启用性能分析
node --prof app.js
# 运行一段时间后 Ctrl+C 停止,生成 isolate-*.log 文件
# 处理分析结果
node --prof-process isolate-*.log > profile.txt
# 查看分析报告
cat profile.txt分析报告关键部分:
code
[Summary]:
ticks total nonlib name
1234 45.2% 52.1% JavaScript
567 20.8% 24.0% C++
234 8.6% 9.9% GC
...v8.getHeapSnapshot 堆快照
javascript
const fs = require('fs');
const v8 = require('v8');
// 生成堆快照
function takeHeapSnapshot() {
const snapshotStream = v8.getHeapSnapshot();
const fileName = `heapdump-${Date.now()}.heapsnapshot`;
const fileStream = fs.createWriteStream(fileName);
snapshotStream.pipe(fileStream);
console.log(`堆快照已保存: ${fileName}`);
return fileName;
}
// 内存泄漏检测
function detectMemoryLeak() {
const before = process.memoryUsage().heapUsed;
// 执行可能泄漏的操作
// ...
// 强制 GC(仅调试用)
if (global.gc) {
global.gc();
}
const after = process.memoryUsage().heapUsed;
const leaked = after - before;
if (leaked > 1024 * 1024) { // 1MB
console.warn(`检测到可能的内存泄漏: ${formatBytes(leaked)}`);
}
}
// 定时生成快照(生产环境慎用)
setInterval(() => {
const used = process.memoryUsage().heapUsed;
const limit = 500 * 1024 * 1024; // 500MB
if (used > limit) {
takeHeapSnapshot();
}
}, 60000);Chrome DevTools 调试
bash
# 启用调试模式
node --inspect app.js
# 或指定端口
node --inspect=9222 app.js
# 生产环境推荐使用 --inspect-brk(启动时暂停)
node --inspect-brk app.jsChrome DevTools 功能说明:
| 面板 | 功能 | 使用场景 |
|---|---|---|
| Profiler | CPU 性能分析 | 定位性能瓶颈 |
| Memory | 内存分析 | 检测内存泄漏 |
| Sources | 代码调试 | 断点调试 |
| Console | 日志输出 | 实时查看日志 |
CPU Profiler 使用步骤:
- 打开 Chrome,访问
chrome://inspect - 点击 "Open dedicated DevTools for Node"
- 切换到 "Profiler" 标签
- 点击 "Start" 开始录制
- 对应用发起请求
- 点击 "Stop" 停止录制
- 分析火焰图定位热点函数
clinic.js 专业诊断工具
安装与基本使用
bash
# 安装 clinic.js
npm install -g clinic
# 或作为项目依赖
npm install clinic --save-devclinic doctor - 诊断建议
bash
# 运行诊断
clinic doctor -- node app.js
# 结合 autocannon 进行压力测试
clinic doctor --on-port 'autocannon -c 10 -d 20 localhost:3000' -- node app.js诊断结果解读:
| 诊断类型 | 说明 | 优化建议 |
|---|---|---|
| I/O 问题 | 存在阻塞 I/O | 使用异步操作 |
| CPU 问题 | CPU 使用过高 | 优化算法复杂度 |
| 内存问题 | 内存持续增长 | 检查内存泄漏 |
| 事件循环延迟 | 事件循环阻塞 | 拆分长任务 |
clinic flame - 火焰图分析
bash
# 生成火焰图
clinic flame -- node app.js
# 配合压力测试
clinic flame --on-port 'autocannon -c 50 -d 30 localhost:3000' -- node app.js火焰图解读:
code
┌─────────────────────────────────────────────────────────────┐
│ 火焰图示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ │
│ │ main() │ │
│ └─────┬─────┘ │
│ ┌───────────────┼───────────────┐ │
│ ┌────┴────┐ ┌─────┴─────┐ ┌────┴────┐ │
│ │ handleA │ │ handleB │ │ handleC │ │
│ └────┬────┘ └─────┬─────┘ └────┬────┘ │
│ ┌───┴───┐ ┌─────┴─────┐ │ │
│ │ calc │ │ process │ │ │
│ └───────┘ └───────────┘ │ │
│ │
│ X轴:函数调用栈 │
│ Y轴:调用深度 │
│ 宽度:执行时间占比 │
│ 颜色:不同模块 │
└─────────────────────────────────────────────────────────────┘clinic bubbleprof - 异步性能分析
bash
# 异步操作分析
clinic bubbleprof -- node app.js
# 分析异步瓶颈
clinic bubbleprof --on-port 'autocannon -c 20 -d 20 localhost:3000' -- node app.js0x 火焰图工具
bash
# 安装 0x
npm install -g 0x
# 生成火焰图
0x app.js
# 指定输出目录
0x -o ./flamegraphs app.js
# 设置采样间隔
0x --sampling-interval 100 app.jstraceview 日志追踪
javascript
const { performance, PerformanceObserver } = require('perf_hooks');
// 性能观察者
const obs = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
console.log({
name: entry.name,
type: entry.entryType,
duration: entry.duration.toFixed(2) + 'ms',
startTime: entry.startTime.toFixed(2) + 'ms'
});
});
});
obs.observe({ entryTypes: ['measure', 'mark', 'function'] });
// 使用示例
performance.mark('start-query');
// 执行数据库查询
// await queryDatabase();
performance.mark('end-query');
performance.measure('database-query', 'start-query', 'end-query');
// 函数性能测量
const measurePerformance = performance.timerify(function heavyComputation(n) {
let result = 0;
for (let i = 0; i < n; i++) {
result += Math.sqrt(i);
}
return result;
});
measurePerformance(1000000);性能优化策略
代码层面优化
1. 异步操作最佳实践
javascript
// ❌ 错误:同步 I/O 阻塞事件循环
const data = fs.readFileSync('large-file.txt');
// ✅ 正确:使用异步 I/O
fs.readFile('large-file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// ✅ 更好:使用 Promise
const { promises: fsPromises } = require('fs');
async function readFileAsync() {
try {
const data = await fsPromises.readFile('large-file.txt', 'utf8');
return data;
} catch (error) {
console.error('读取文件失败:', error);
throw error;
}
}
// ✅ 批量文件处理
async function readMultipleFiles(filePaths) {
const promises = filePaths.map(path =>
fsPromises.readFile(path, 'utf8')
);
return Promise.all(promises);
}2. 避免阻塞事件循环
javascript
// ❌ 错误:CPU 密集型操作阻塞事件循环
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// ✅ 解决方案 1:使用 setImmediate 分片执行
function fibonacciAsync(n, callback) {
if (n <= 1) {
setImmediate(() => callback(n));
return;
}
setImmediate(() => {
fibonacciAsync(n - 1, (result1) => {
fibonacciAsync(n - 2, (result2) => {
callback(result1 + result2);
});
});
});
}
// ✅ 解决方案 2:使用 Worker Threads
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
function fibonacciParallel(n) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, { workerData: n });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
});
});
}
// 使用
fibonacciParallel(40).then(console.log);
} else {
// Worker 线程代码
const result = fibonacci(workerData);
parentPort.postMessage(result);
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
}3. 缓存策略实现
javascript
/**
* 多级缓存实现
*/
class MultiLevelCache {
constructor(options = {}) {
this.memoryCache = new Map();
this.maxSize = options.maxSize || 1000;
this.ttl = options.ttl || 60000; // 默认 1 分钟
}
/**
* 获取缓存
* @param {string} key - 缓存键
* @param {Function} fetcher - 数据获取函数
* @returns {Promise<any>} 缓存数据
*/
async get(key, fetcher) {
// 检查内存缓存
if (this.memoryCache.has(key)) {
const cached = this.memoryCache.get(key);
// 检查是否过期
if (Date.now() < cached.expiry) {
return cached.value;
}
// 过期则删除
this.memoryCache.delete(key);
}
// 获取新数据
const value = await fetcher();
// 存入缓存
this.set(key, value);
return value;
}
/**
* 设置缓存
*/
set(key, value, customTtl) {
// LRU 淘汰策略
if (this.memoryCache.size >= this.maxSize) {
const oldestKey = this.memoryCache.keys().next().value;
this.memoryCache.delete(oldestKey);
}
this.memoryCache.set(key, {
value,
expiry: Date.now() + (customTtl || this.ttl)
});
}
/**
* 删除缓存
*/
delete(key) {
this.memoryCache.delete(key);
}
/**
* 清空缓存
*/
clear() {
this.memoryCache.clear();
}
/**
* 获取缓存统计
*/
getStats() {
return {
size: this.memoryCache.size,
maxSize: this.maxSize,
keys: Array.from(this.memoryCache.keys())
};
}
}
// 使用示例
const cache = new MultiLevelCache({ maxSize: 500, ttl: 300000 });
async function getUser(userId) {
return cache.get(`user:${userId}`, async () => {
// 从数据库获取
return await db.users.findById(userId);
});
}4. 算法复杂度优化
javascript
// ❌ O(n²) 复杂度
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
duplicates.push(arr[i]);
}
}
}
return duplicates;
}
// ✅ O(n) 复杂度
function findDuplicatesOptimized(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) {
duplicates.add(item);
} else {
seen.add(item);
}
}
return Array.from(duplicates);
}
// 大数组处理示例
function processLargeArray(array, chunkSize = 1000) {
const results = [];
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = array.slice(i, i + chunkSize);
// 使用 setImmediate 避免阻塞
setImmediate(() => {
const processed = chunk.map(item => transform(item));
results.push(...processed);
});
}
return results;
}5. JSON 处理优化
javascript
// 大 JSON 文件流式处理
const { chain } = require('stream-chain');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
// 流式处理大型 JSON 文件
async function processLargeJSON(filePath) {
const pipeline = chain([
fs.createReadStream(filePath),
parser(),
streamArray(),
// 自定义处理逻辑
data => {
const { key, value } = data;
// 处理每条数据
return processItem(value);
}
]);
const results = [];
return new Promise((resolve, reject) => {
pipeline.on('data', result => results.push(result));
pipeline.on('end', () => resolve(results));
pipeline.on('error', reject);
});
}
// JSON 序列化优化
function safeJSONStringify(obj, space = 0) {
const cache = new Set();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.has(value)) {
// 循环引用处理
return '[Circular]';
}
cache.add(value);
}
return value;
}, space);
}架构层面优化
1. 负载均衡配置
javascript
// Node.js 集群模式
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const cpuCount = os.cpus().length;
console.log(`主进程 ${process.pid} 启动`);
console.log(`启动 ${cpuCount} 个工作进程`);
// 根据 CPU 核心数创建工作进程
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
// 工作进程退出时重启
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 退出: ${signal || code}`);
console.log('启动新的工作进程...');
cluster.fork();
});
// 平滑重启
process.on('SIGUSR2', () => {
const workers = Object.values(cluster.workers);
let index = 0;
function restartWorker() {
if (index < workers.length) {
const worker = workers[index];
console.log(`重启工作进程 ${worker.process.pid}`);
worker.disconnect();
worker.on('exit', () => {
if (!worker.exitedAfterDisconnect) return;
const newWorker = cluster.fork();
newWorker.on('listening', () => {
index++;
restartWorker();
});
});
}
}
restartWorker();
});
} else {
// 工作进程代码
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send(`工作进程 ${process.pid} 响应`);
});
app.listen(3000, () => {
console.log(`工作进程 ${process.pid} 监听端口 3000`);
});
}2. 数据库连接池配置
javascript
const { Pool } = require('pg');
// PostgreSQL 连接池配置
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
// 连接池参数
max: 20, // 最大连接数
min: 2, // 最小连接数
idleTimeoutMillis: 30000, // 空闲超时
connectionTimeoutMillis: 2000, // 连接超时
// 连接健康检查
testOnBorrow: true,
acquireTimeoutMillis: 30000
});
// 连接池监控
pool.on('connect', () => {
console.log('新客户端连接');
});
pool.on('remove', () => {
console.log('客户端断开连接');
});
pool.on('error', (err) => {
console.error('连接池错误:', err);
});
// 封装查询方法
async function query(text, params) {
const start = Date.now();
const result = await pool.query(text, params);
const duration = Date.now() - start;
console.log('执行查询:', {
text,
duration: `${duration}ms`,
rows: result.rowCount
});
return result;
}
// 使用示例
async function getUsers() {
try {
const result = await query('SELECT * FROM users WHERE id = $1', [userId]);
return result.rows;
} catch (error) {
console.error('查询失败:', error);
throw error;
}
}3. Redis 缓存策略
javascript
const Redis = require('ioredis');
// Redis 客户端配置
const redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD,
db: process.env.REDIS_DB || 0,
// 连接池配置
maxRetriesPerRequest: 3,
enableReadyCheck: true,
enableOfflineQueue: true,
// 重连策略
retryStrategy: (times) => {
if (times > 10) {
console.error('Redis 连接失败次数过多');
return null;
}
const delay = Math.min(times * 50, 2000);
return delay;
}
});
// 缓存装饰器
function cacheable(keyPrefix, ttl = 60) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args) {
const key = `${keyPrefix}:${JSON.stringify(args)}`;
// 尝试从缓存获取
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
// 执行原方法
const result = await originalMethod.apply(this, args);
// 存入缓存
await redis.setex(key, ttl, JSON.stringify(result));
return result;
};
return descriptor;
};
}
// 使用示例
class UserService {
@cacheable('user', 300) // 缓存 5 分钟
async getUserById(userId) {
return await db.users.findById(userId);
}
// 手动缓存控制
async updateUser(userId, data) {
const result = await db.users.update(userId, data);
// 清除缓存
await redis.del(`user:${userId}`);
return result;
}
}
// 分布式锁实现
class DistributedLock {
constructor(redis) {
this.redis = redis;
}
async acquire(key, ttl = 10) {
const token = Math.random().toString(36).substr(2);
const result = await this.redis.set(key, token, 'NX', 'EX', ttl);
if (result === 'OK') {
return token;
}
return null;
}
async release(key, token) {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
await this.redis.eval(script, 1, key, token);
}
}
// 使用分布式锁
const lock = new DistributedLock(redis);
async function processOrder(orderId) {
const token = await lock.acquire(`order:${orderId}`);
if (!token) {
throw new Error('订单正在处理中');
}
try {
// 处理订单逻辑
await doProcess(orderId);
} finally {
await lock.release(`order:${orderId}`, token);
}
}4. CDN 加速配置
javascript
// Express 静态资源配置 CDN
const express = require('express');
const app = express();
// CDN 配置
const CDN_CONFIG = {
enabled: process.env.NODE_ENV === 'production',
baseUrl: process.env.CDN_URL || 'https://cdn.example.com',
assets: ['css', 'js', 'images', 'fonts']
};
// 静态资源中间件
function cdnMiddleware(req, res, next) {
if (!CDN_CONFIG.enabled) {
return next();
}
const ext = req.path.split('.').pop()?.toLowerCase();
if (CDN_CONFIG.assets.includes(ext)) {
// 重定向到 CDN
return res.redirect(301, CDN_CONFIG.baseUrl + req.path);
}
next();
}
// 缓存控制
app.use(express.static('public', {
maxAge: '1y', // 静态资源缓存 1 年
etag: true, // 启用 ETag
lastModified: true, // 启用 Last-Modified
setHeaders: (res, path) => {
// HTML 文件不缓存
if (path.endsWith('.html')) {
res.set('Cache-Control', 'no-cache');
}
}
}));
// 图片优化响应头
app.get('/images/*', (req, res, next) => {
res.set({
'Cache-Control': 'public, max-age=31536000',
'Content-Type': 'image/webp'
});
next();
});资源层面优化
1. 内存管理最佳实践
javascript
/**
* 内存监控和管理
*/
class MemoryManager {
constructor(options = {}) {
this.warningThreshold = options.warningThreshold || 0.8; // 80%
this.criticalThreshold = options.criticalThreshold || 0.9; // 90%
this.checkInterval = options.checkInterval || 30000; // 30 秒
this.startMonitoring();
}
/**
* 获取堆内存使用比例
*/
getHeapUsageRatio() {
const used = process.memoryUsage();
return used.heapUsed / used.heapTotal;
}
/**
* 启动监控
*/
startMonitoring() {
setInterval(() => {
const ratio = this.getHeapUsageRatio();
const used = process.memoryUsage();
if (ratio > this.criticalThreshold) {
console.error('⚠️ 内存使用严重过高!', {
heapUsed: formatBytes(used.heapUsed),
heapTotal: formatBytes(used.heapTotal),
ratio: (ratio * 100).toFixed(2) + '%'
});
// 触发紧急处理
this.handleCriticalMemory();
} else if (ratio > this.warningThreshold) {
console.warn('⚠️ 内存使用警告', {
heapUsed: formatBytes(used.heapUsed),
heapTotal: formatBytes(used.heapTotal),
ratio: (ratio * 100).toFixed(2) + '%'
});
}
}, this.checkInterval);
}
/**
* 紧急内存处理
*/
handleCriticalMemory() {
// 1. 清理全局缓存
if (global.cache) {
global.cache.clear();
}
// 2. 触发 GC(如果可用)
if (global.gc) {
global.gc();
}
// 3. 记录内存快照
this.captureMemorySnapshot();
}
/**
* 内存快照
*/
captureMemorySnapshot() {
const v8 = require('v8');
const fs = require('fs');
const snapshot = v8.writeHeapSnapshot();
console.log(`内存快照已保存: ${snapshot}`);
}
}
// 使用示例
const memoryManager = new MemoryManager({
warningThreshold: 0.75,
criticalThreshold: 0.85,
checkInterval: 60000
});2. 连接池管理
javascript
/**
* 通用连接池实现
*/
class ConnectionPool {
constructor(options = {}) {
this.factory = options.factory;
this.maxSize = options.maxSize || 10;
this.minSize = options.minSize || 2;
this.idleTimeout = options.idleTimeout || 30000;
this.connections = [];
this.available = [];
this.waitQueue = [];
this.initialize();
}
async initialize() {
// 创建最小连接数
for (let i = 0; i < this.minSize; i++) {
const conn = await this.factory.create();
this.connections.push(conn);
this.available.push(conn);
}
}
async acquire() {
// 有可用连接
if (this.available.length > 0) {
return this.available.pop();
}
// 创建新连接
if (this.connections.length < this.maxSize) {
const conn = await this.factory.create();
this.connections.push(conn);
return conn;
}
// 等待可用连接
return new Promise((resolve) => {
this.waitQueue.push(resolve);
});
}
release(conn) {
if (this.waitQueue.length > 0) {
const next = this.waitQueue.shift();
next(conn);
} else {
this.available.push(conn);
}
}
async destroy(conn) {
await this.factory.destroy(conn);
const index = this.connections.indexOf(conn);
if (index > -1) {
this.connections.splice(index, 1);
}
}
async drain() {
for (const conn of this.connections) {
await this.factory.destroy(conn);
}
this.connections = [];
this.available = [];
}
}
// 使用示例
const pool = new ConnectionPool({
maxSize: 20,
minSize: 5,
factory: {
create: async () => {
// 创建连接
return await createConnection();
},
destroy: async (conn) => {
// 销毁连接
await conn.close();
}
}
});3. 文件描述符限制
bash
# 查看当前限制
ulimit -n
# 临时修改限制
ulimit -n 65535
# 永久修改(/etc/security/limits.conf)
* soft nofile 65535
* hard nofile 65535
# Node.js 启动时设置
NODE_OPTIONS="--max-old-space-size=4096" node app.jsjavascript
// Node.js 中监控文件描述符
const fs = require('fs');
const path = require('path');
function getOpenFileDescriptors() {
try {
const fdDir = '/proc/self/fd';
const fds = fs.readdirSync(fdDir);
return fds.length;
} catch (error) {
return -1;
}
}
// 监控打开的文件描述符
setInterval(() => {
const count = getOpenFileDescriptors();
console.log(`打开的文件描述符: ${count}`);
}, 10000);监控与告警
Prometheus + Grafana 监控方案
javascript
const client = require('prom-client');
// 创建 Registry
const register = new client.Registry();
// 默认指标
client.collectDefaultMetrics({ register });
// 自定义指标
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP 请求响应时间',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 1, 3, 5, 10],
registers: [register]
});
const httpRequestTotal = new client.Counter({
name: 'http_requests_total',
help: 'HTTP 请求总数',
labelNames: ['method', 'route', 'status_code'],
registers: [register]
});
const activeConnections = new client.Gauge({
name: 'active_connections',
help: '活跃连接数',
registers: [register]
});
// Express 集成
const express = require('express');
const app = express();
// 指标端点
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// 监控中间件
app.use((req, res, next) => {
const start = Date.now();
const route = req.route?.path || req.path;
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration
.labels(req.method, route, res.statusCode)
.observe(duration);
httpRequestTotal
.labels(req.method, route, res.statusCode)
.inc();
});
next();
});告警规则配置
yaml
# prometheus/alert.rules.yml
groups:
- name: nodejs_alerts
rules:
# 响应时间告警
- alert: HighResponseTime
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "高响应时间警告"
description: "P95 响应时间超过 500ms"
# 错误率告警
- alert: HighErrorRate
expr: rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "高错误率警告"
description: "5xx 错误率超过 5%"
# 内存使用告警
- alert: HighMemoryUsage
expr: process_resident_memory_bytes / process_virtual_memory_bytes > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "内存使用过高"
description: "内存使用超过 90%"常见问题解答
Q1: 如何排查 Node.js 应用响应慢的问题?
排查步骤:
-
确认问题范围
- 是所有接口慢还是特定接口?
- 是偶发还是持续?
-
检查系统资源
bash# CPU 使用情况 top -pid <node_pid> # 内存使用情况 ps -o rss= -p <node_pid> # 查看事件循环延迟 node -e "console.log(require('perf_hooks').performance.eventLoopUtilization())" -
使用分析工具
bash# CPU 分析 node --prof app.js node --prof-process isolate-*.log # 使用 clinic.js clinic doctor -- node app.js -
检查数据库
sql-- MySQL 慢查询 SHOW VARIABLES LIKE 'slow_query_log%';
Q2: 如何处理内存泄漏?
诊断方法:
javascript
// 1. 启用 GC 日志
// node --expose-gc --trace_gc app.js
// 2. 定期获取内存快照
const v8 = require('v8');
setInterval(() => {
const snapshot = v8.writeHeapSnapshot();
console.log(`快照: ${snapshot}`);
}, 300000); // 每 5 分钟
// 3. 使用 Chrome DevTools 比较快照
// 找出持续增长的对象常见泄漏原因:
| 原因 | 示例 | 解决方案 |
|---|---|---|
| 全局变量 | global.data = [] | 避免全局变量,使用模块作用域 |
| 未清理的定时器 | setInterval | 确保 clearInterval |
| 闭包引用 | 事件监听器 | 及时移除监听器 |
| 缓存无限制 | 无限增长的 Map | 使用 LRU 缓存 |
Q3: 如何优化大量并发请求?
javascript
// 1. 使用集群模式
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
}
// 2. 连接池复用
const pool = new Pool({ max: 20 });
// 3. 使用流式处理
const { pipeline } = require('stream');
pipeline(
sourceStream,
transformStream,
destinationStream,
callback
);
// 4. 限流保护
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100 // 限制 100 次请求
});
app.use(limiter);Q4: 生产环境推荐的 Node.js 启动参数?
bash
#!/bin/bash
# 推荐的生产环境启动配置
NODE_ENV=production \
node \
--max-old-space-size=4096 \ # 最大堆内存 4GB
--max-semi-space-size=512 \ # 新生代半空间大小
--optimize-for-size \ # 优化内存而非速度
--gc-interval=100 \ # GC 间隔
--max-http-header-size=16384 \ # HTTP 头最大尺寸
--enable-source-maps \ # 启用源码映射
--unhandled-rejections=strict \ # 未处理 Promise 严格模式
app.js最佳实践
性能优化检查清单
开发阶段
- 使用异步 API 替代同步 API
- 避免在热点路径进行复杂计算
- 合理使用缓存策略
- 正确处理错误,避免进程崩溃
- 使用流式处理大文件
- 限制并发数量
测试阶段
- 进行压力测试和负载测试
- 使用性能分析工具定位瓶颈
- 检测内存泄漏
- 验证集群模式
- 测试异常恢复能力
部署阶段
- 启用集群模式
- 配置合理的内存限制
- 设置日志和监控
- 配置健康检查端点
- 准备降级方案
运维阶段
- 持续监控关键指标
- 设置合理的告警阈值
- 定期性能审查
- 建立性能基线
- 记录性能变化历史
性能优化决策流程
code
┌─────────────────────────────────────────────────────────────────┐
│ 性能优化决策流程 │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ 发现性能问题 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 确认问题范围 │
└────────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ CPU 问题 │ │ 内存问题 │ │ I/O 问题 │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│• 优化算法 │ │• 检查泄漏 │ │• 使用缓存 │
│• 使用Worker│ │• 限制大小 │ │• 异步处理 │
│• 减少计算 │ │• 及时释放 │ │• 连接池 │
└───────────┘ └───────────┘ └───────────┘
│
▼
┌─────────────────┐
│ 验证优化效果 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 持续监控改进 │
└─────────────────┘关键指标参考值
| 指标 | 优秀 | 良好 | 警告 | 严重 |
|---|---|---|---|---|
| 平均响应时间 | < 100ms | < 300ms | < 1000ms | > 1000ms |
| P95 响应时间 | < 200ms | < 500ms | < 2000ms | > 2000ms |
| 错误率 | < 0.1% | < 1% | < 5% | > 5% |
| CPU 使用率 | < 50% | < 70% | < 85% | > 85% |
| 内存使用率 | < 60% | < 75% | < 90% | > 90% |
| 事件循环延迟 | < 10ms | < 50ms | < 100ms | > 100ms |
参考资源
Node.js 22+ 性能优化新特性
模块编译缓存
Node.js 22.1+ 实验性、24+ 稳定的模块编译缓存,显著加速应用启动:
bash
# 启用模块编译缓存
NODE_COMPILE_CACHE=/tmp/node-cache node app.js
# 效果:二次启动速度提升 20-60%process.availableMemory 内存感知
javascript
// Node.js 22+ 获取可用内存
const available = process.availableMemory()
const constrained = process.constrainedMemory()
// 根据可用内存动态调整策略
if (available < 100 * 1024 * 1024) {
console.warn('可用内存不足 100MB,启用低内存模式')
cache.setMaxEntries(100)
} else {
cache.setMaxEntries(10000)
}原生 --watch 加速开发迭代
bash
# 传统方式(nodemon 有启动延迟)
npx nodemon app.js
# Node.js 22+ 原生方式(更快响应文件变更)
node --watch app.js权限模型减少攻击面
bash
# 限制资源访问,减少潜在性能问题
node --permission \
--allow-fs-read=/app \
--allow-net=0.0.0.0:3000 \
app.jsWeb Streams 流式处理
javascript
// 使用 Web Streams 处理大数据,避免内存溢出
const response = await fetch('https://api.example.com/large-dataset')
const reader = response.body.getReader()
const decoder = new TextDecoder()
let totalSize = 0
while (true) {
const { done, value } = await reader.read()
if (done) break
totalSize += value.length
processChunk(decoder.decode(value, { stream: true }))
}
console.log(`处理完成: ${totalSize} bytes`)