Node.js后端

Node.js 性能监控与调优

2024-11-10
后端

为什么要做性能监控?

Node.js 应用在生产环境中可能遇到各种问题:内存泄漏、CPU 占用过高、响应变慢等。建立完善的监控体系是保障服务稳定性的关键。

监控指标

1. 系统指标

  • CPU 使用率
  • 内存使用情况
  • 磁盘 I/O
  • 网络流量
  • 2. 应用指标

  • 请求 QPS
  • 响应时间(P50/P90/P99)
  • 错误率
  • 事件循环延迟
  • 3. 业务指标

  • 活跃用户数
  • 数据库连接数
  • 缓存命中率
  • 内存泄漏排查

    1. 使用 heapdump

    const heapdump = require('heapdump');
    
    // 定期生成堆快照
    setInterval(() => {
      heapdump.writeSnapshot(`/tmp/${Date.now()}.heapsnapshot`);
    }, 60 * 60 * 1000);

    2. 使用 Chrome DevTools

  • 启动应用时添加 --inspect 参数
  • 打开 Chrome,访问 chrome://inspect
  • 连接到远程进程
  • 使用 Memory 面板分析堆快照
  • 3. 常见内存泄漏场景

    // 1. 全局变量
    const cache = {}; // 无限增长
    
    // 2. 闭包引用
    function createHandler() {
      const largeData = new Array(1000000);
      return () => {
        console.log(largeData.length); // largeData 永远不会被释放
      };
    }
    
    // 3. 未清理的定时器
    setInterval(() => {
      // 一些操作
    }, 1000); // 忘记 clearInterval

    CPU Profiling

    1. 使用 --prof 参数

    node --prof app.js
    # 生成 isolate-*.log 文件
    node --prof-process isolate-*.log > processed.txt

    2. 使用 clinic

    npm install -g clinic
    clinic doctor -- node app.js
    clinic flame -- node app.js

    3. 使用 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 工具

  • New Relic
  • Datadog
  • Elastic APM
  • 总结

    Node.js 性能监控与调优的关键:

  • **建立监控体系**:覆盖系统、应用、业务指标
  • **定期分析**:使用工具分析内存和 CPU
  • **避免阻塞**:将耗时操作移到 Worker
  • **持续优化**:性能优化是一个持续的过程
  • 记住,预防胜于治疗。在开发阶段就注意性能问题,比在生产环境中救火要好得多。