{T}

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();
}

吞吐量指标

吞吐量反映系统的处理能力,是容量规划的关键依据。

指标详解

指标名称英文全称定义说明典型值范围
QPSQueries Per Second每秒查询数,适用于读操作1000-10000+
TPSTransactions 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
autocannonNode.js 原生,功能丰富Node.js 应用测试npm install -g autocannon
k6脚本化,支持 CI/CD自动化测试流程brew install k6
ArtilleryYAML 配置,功能完整复杂场景测试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.json

config.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.js

Chrome DevTools 功能说明:

面板功能使用场景
ProfilerCPU 性能分析定位性能瓶颈
Memory内存分析检测内存泄漏
Sources代码调试断点调试
Console日志输出实时查看日志

CPU Profiler 使用步骤:

  1. 打开 Chrome,访问 chrome://inspect
  2. 点击 "Open dedicated DevTools for Node"
  3. 切换到 "Profiler" 标签
  4. 点击 "Start" 开始录制
  5. 对应用发起请求
  6. 点击 "Stop" 停止录制
  7. 分析火焰图定位热点函数

clinic.js 专业诊断工具

安装与基本使用

bash
# 安装 clinic.js
npm install -g clinic

# 或作为项目依赖
npm install clinic --save-dev

clinic 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.js

0x 火焰图工具

bash
# 安装 0x
npm install -g 0x

# 生成火焰图
0x app.js

# 指定输出目录
0x -o ./flamegraphs app.js

# 设置采样间隔
0x --sampling-interval 100 app.js

traceview 日志追踪

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.js
javascript
// 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 应用响应慢的问题?

排查步骤:

  1. 确认问题范围

    • 是所有接口慢还是特定接口?
    • 是偶发还是持续?
  2. 检查系统资源

    bash
    # CPU 使用情况
    top -pid <node_pid>
    
    # 内存使用情况
    ps -o rss= -p <node_pid>
    
    # 查看事件循环延迟
    node -e "console.log(require('perf_hooks').performance.eventLoopUtilization())"
  3. 使用分析工具

    bash
    # CPU 分析
    node --prof app.js
    node --prof-process isolate-*.log
    
    # 使用 clinic.js
    clinic doctor -- node app.js
  4. 检查数据库

    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.js

Web 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`)