Node.js 性能优化完全指南:从基础到高级

Node.js 性能优化 内存管理 集群部署 后端开发 JavaScript

Node.js的性能优化是构建高效后端应用的关键。本文将深入探讨内存管理、CPU优化、I/O优化、集群部署等核心话题,提供实用的优化策略和最佳实践,帮助您构建高性能的Node.js应用。

Node.js 性能基础

Node.js基于V8 JavaScript引擎和libuv事件循环,理解这些底层机制对性能优化至关重要:

// 事件循环的基本理解
console.log('同步操作 1');

setImmediate(() => {
    console.log('setImmediate');
});

setTimeout(() => {
    console.log('setTimeout');
}, 0);

process.nextTick(() => {
    console.log('nextTick');
});

Promise.resolve().then(() => {
    console.log('Promise');
});

console.log('同步操作 2');

// 输出顺序:
// 同步操作 1
// 同步操作 2
// nextTick
// Promise
// setTimeout
// setImmediate

1. 内存优化

内存泄漏检测与预防

// ❌ 内存泄漏示例
const memoryLeaks = [];

function createLeak() {
    const data = new Array(1000000).fill('memory leak');
    memoryLeaks.push(data); // 数组持续增长,永不释放
}

// ✅ 正确的内存管理
class DataCache {
    constructor(maxSize = 1000) {
        this.cache = new Map();
        this.maxSize = maxSize;
    }
    
    set(key, value) {
        // 实现LRU缓存策略
        if (this.cache.size >= this.maxSize) {
            const firstKey = this.cache.keys().next().value;
            this.cache.delete(firstKey);
        }
        this.cache.set(key, value);
    }
    
    get(key) {
        const value = this.cache.get(key);
        if (value !== undefined) {
            // 更新访问顺序
            this.cache.delete(key);
            this.cache.set(key, value);
        }
        return value;
    }
    
    clear() {
        this.cache.clear();
    }
}

// 监控内存使用
function monitorMemory() {
    const usage = process.memoryUsage();
    console.log({
        rss: Math.round(usage.rss / 1024 / 1024) + ' MB',
        heapTotal: Math.round(usage.heapTotal / 1024 / 1024) + ' MB',
        heapUsed: Math.round(usage.heapUsed / 1024 / 1024) + ' MB',
        external: Math.round(usage.external / 1024 / 1024) + ' MB'
    });
}

setInterval(monitorMemory, 5000);

对象池和缓存优化

// 对象池实现
class ObjectPool {
    constructor(createFn, resetFn, initialSize = 10) {
        this.createFn = createFn;
        this.resetFn = resetFn;
        this.pool = [];
        
        // 预创建对象
        for (let i = 0; i < initialSize; i++) {
            this.pool.push(this.createFn());
        }
    }
    
    acquire() {
        return this.pool.length > 0 ? this.pool.pop() : this.createFn();
    }
    
    release(obj) {
        this.resetFn(obj);
        this.pool.push(obj);
    }
    
    size() {
        return this.pool.length;
    }
}

// 使用示例
const bufferPool = new ObjectPool(
    () => Buffer.allocUnsafe(1024),
    (buffer) => buffer.fill(0),
    50
);

function processData(data) {
    const buffer = bufferPool.acquire();
    try {
        // 处理数据
        buffer.write(data);
        return buffer.toString();
    } finally {
        bufferPool.release(buffer);
    }
}

// WeakMap用于避免内存泄漏
const cache = new WeakMap();

function expensiveOperation(obj) {
    if (cache.has(obj)) {
        return cache.get(obj);
    }
    
    const result = performExpensiveCalculation(obj);
    cache.set(obj, result);
    return result;
}

2. CPU 优化

避免阻塞事件循环

// ❌ 阻塞事件循环
function heavyTask() {
    const start = Date.now();
    while (Date.now() - start < 5000) {
        // 5秒的同步计算
    }
    return 'Task completed';
}

// ✅ 使用异步分块处理
function heavyTaskAsync(data, callback) {
    const chunkSize = 1000;
    let index = 0;
    
    function processChunk() {
        const endIndex = Math.min(index + chunkSize, data.length);
        
        // 处理当前块
        for (let i = index; i < endIndex; i++) {
            // 处理 data[i]
        }
        
        index = endIndex;
        
        if (index < data.length) {
            // 让出控制权给其他任务
            setImmediate(processChunk);
        } else {
            callback('Task completed');
        }
    }
    
    processChunk();
}

// ✅ 使用Worker Threads处理CPU密集型任务
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
    // 主线程
    function createWorker(data) {
        return new Promise((resolve, reject) => {
            const worker = new Worker(__filename, {
                workerData: data
            });
            
            worker.on('message', resolve);
            worker.on('error', reject);
            worker.on('exit', (code) => {
                if (code !== 0) {
                    reject(new Error(`Worker stopped with exit code ${code}`));
                }
            });
        });
    }
    
    // 使用Worker
    async function processInWorker(heavyData) {
        try {
            const result = await createWorker(heavyData);
            console.log('结果:', result);
        } catch (error) {
            console.error('Worker错误:', error);
        }
    }
} else {
    // Worker线程
    function heavyComputation(data) {
        // CPU密集型计算
        let result = 0;
        for (let i = 0; i < data.length; i++) {
            result += Math.sqrt(data[i]);
        }
        return result;
    }
    
    const result = heavyComputation(workerData);
    parentPort.postMessage(result);
}

性能监控和分析

// 性能监控中间件
function performanceMiddleware(req, res, next) {
    const start = process.hrtime.bigint();
    
    res.on('finish', () => {
        const duration = process.hrtime.bigint() - start;
        const ms = Number(duration) / 1000000; // 转换为毫秒
        
        console.log(`${req.method} ${req.url} - ${ms.toFixed(2)}ms`);
        
        // 记录慢请求
        if (ms > 1000) {
            console.warn(`慢请求警告: ${req.url} 耗时 ${ms.toFixed(2)}ms`);
        }
    });
    
    next();
}

// 使用性能分析
const { performance, PerformanceObserver } = require('perf_hooks');

const obs = new PerformanceObserver((list) => {
    list.getEntries().forEach((entry) => {
        console.log(`${entry.name}: ${entry.duration}ms`);
    });
});
obs.observe({ entryTypes: ['measure'] });

// 标记性能点
function expensiveFunction() {
    performance.mark('expensive-start');
    
    // 一些耗时操作
    for (let i = 0; i < 1000000; i++) {
        Math.random();
    }
    
    performance.mark('expensive-end');
    performance.measure('expensive-function', 'expensive-start', 'expensive-end');
}

3. I/O 优化

文件系统优化

const fs = require('fs').promises;
const path = require('path');

// ✅ 使用流处理大文件
const { createReadStream, createWriteStream } = require('fs');

function copyLargeFile(source, destination) {
    return new Promise((resolve, reject) => {
        const readStream = createReadStream(source);
        const writeStream = createWriteStream(destination);
        
        readStream.pipe(writeStream);
        
        writeStream.on('finish', resolve);
        writeStream.on('error', reject);
        readStream.on('error', reject);
    });
}

// ✅ 批量文件操作
async function processFilesInBatches(files, batchSize = 10) {
    const results = [];
    
    for (let i = 0; i < files.length; i += batchSize) {
        const batch = files.slice(i, i + batchSize);
        const batchPromises = batch.map(async (file) => {
            try {
                const data = await fs.readFile(file, 'utf8');
                return { file, success: true, data };
            } catch (error) {
                return { file, success: false, error: error.message };
            }
        });
        
        const batchResults = await Promise.all(batchPromises);
        results.push(...batchResults);
    }
    
    return results;
}

// ✅ 文件缓存
class FileCache {
    constructor(maxAge = 5 * 60 * 1000) { // 5分钟
        this.cache = new Map();
        this.maxAge = maxAge;
    }
    
    async get(filePath) {
        const cached = this.cache.get(filePath);
        
        if (cached && Date.now() - cached.timestamp < this.maxAge) {
            return cached.data;
        }
        
        try {
            const stats = await fs.stat(filePath);
            const data = await fs.readFile(filePath, 'utf8');
            
            this.cache.set(filePath, {
                data,
                timestamp: Date.now(),
                mtime: stats.mtime
            });
            
            return data;
        } catch (error) {
            this.cache.delete(filePath);
            throw error;
        }
    }
    
    clear() {
        this.cache.clear();
    }
}

数据库连接优化

// 连接池配置
const mysql = require('mysql2/promise');

const pool = mysql.createPool({
    host: 'localhost',
    user: 'username',
    password: 'password',
    database: 'database',
    waitForConnections: true,
    connectionLimit: 10,
    queueLimit: 0,
    acquireTimeout: 60000,
    timeout: 60000,
    reconnect: true
});

// ✅ 查询优化
class DatabaseOptimizer {
    constructor(pool) {
        this.pool = pool;
        this.queryCache = new Map();
    }
    
    async query(sql, params = []) {
        const cacheKey = sql + JSON.stringify(params);
        
        // 简单的查询缓存(适用于只读查询)
        if (this.queryCache.has(cacheKey)) {
            const cached = this.queryCache.get(cacheKey);
            if (Date.now() - cached.timestamp < 30000) { // 30秒缓存
                return cached.result;
            }
        }
        
        try {
            const [rows] = await this.pool.execute(sql, params);
            
            // 缓存SELECT查询结果
            if (sql.trim().toUpperCase().startsWith('SELECT')) {
                this.queryCache.set(cacheKey, {
                    result: rows,
                    timestamp: Date.now()
                });
            }
            
            return rows;
        } catch (error) {
            console.error('数据库查询错误:', error);
            throw error;
        }
    }
    
    // 批量插入优化
    async batchInsert(table, data, batchSize = 1000) {
        if (!data.length) return;
        
        const columns = Object.keys(data[0]);
        const placeholders = columns.map(() => '?').join(',');
        const sql = `INSERT INTO ${table} (${columns.join(',')}) VALUES (${placeholders})`;
        
        const results = [];
        for (let i = 0; i < data.length; i += batchSize) {
            const batch = data.slice(i, i + batchSize);
            const values = batch.map(row => columns.map(col => row[col]));
            
            try {
                const batchResults = await Promise.all(
                    values.map(row => this.pool.execute(sql, row))
                );
                results.push(...batchResults);
            } catch (error) {
                console.error(`批量插入失败 (批次 ${i}-${i + batch.length}):`, error);
                throw error;
            }
        }
        
        return results;
    }
}

4. 网络和HTTP优化

const express = require('express');
const compression = require('compression');
const helmet = require('helmet');

const app = express();

// ✅ 启用压缩
app.use(compression({
    level: 6,
    threshold: 1024,
    filter: (req, res) => {
        if (req.headers['x-no-compression']) {
            return false;
        }
        return compression.filter(req, res);
    }
}));

// ✅ 安全头
app.use(helmet());

// ✅ 请求限流
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15分钟
    max: 100, // 限制每个IP 100个请求
    message: '请求过于频繁,请稍后再试',
    standardHeaders: true,
    legacyHeaders: false
});

app.use('/api/', limiter);

// ✅ 缓存控制
function setCacheHeaders(req, res, next) {
    if (req.url.match(/\.(css|js|png|jpg|jpeg|gif|ico|svg)$/)) {
        res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1年
    } else if (req.url.startsWith('/api/')) {
        res.setHeader('Cache-Control', 'no-cache');
    }
    next();
}

app.use(setCacheHeaders);

// ✅ HTTP/2 和 Keep-Alive
const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
    key: fs.readFileSync('private-key.pem'),
    cert: fs.readFileSync('certificate.pem')
});

server.on('stream', (stream, headers) => {
    if (headers[':method'] === 'GET') {
        stream.respond({
            'content-type': 'text/html',
            ':status': 200
        });
        stream.end('<h1>Hello HTTP/2!</h1>');
    }
});

// ✅ 连接池优化
const http = require('http');
const https = require('https');

const httpAgent = new http.Agent({
    keepAlive: true,
    maxSockets: 50,
    maxFreeSockets: 10,
    timeout: 60000
});

const httpsAgent = new https.Agent({
    keepAlive: true,
    maxSockets: 50,
    maxFreeSockets: 10,
    timeout: 60000
});

// 在请求中使用代理
const axios = require('axios');

const client = axios.create({
    httpAgent,
    httpsAgent,
    timeout: 30000
});

5. 集群和部署优化

// ✅ 集群模式
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);
    
    // 创建工作进程
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
    
    cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
        console.log('重新启动工作进程...');
        cluster.fork();
    });
    
    // 优雅关闭
    process.on('SIGTERM', () => {
        console.log('收到SIGTERM信号,正在关闭...');
        for (const id in cluster.workers) {
            cluster.workers[id].kill();
        }
    });
} else {
    // 工作进程
    const app = require('./app');
    const server = app.listen(3000, () => {
        console.log(`工作进程 ${process.pid} 在端口 3000 上启动`);
    });
    
    // 优雅关闭
    process.on('SIGTERM', () => {
        console.log('工作进程收到SIGTERM信号');
        server.close(() => {
            console.log('HTTP服务器已关闭');
            process.exit(0);
        });
    });
}

// ✅ PM2 配置示例 (ecosystem.config.js)
module.exports = {
    apps: [{
        name: 'my-app',
        script: 'app.js',
        instances: 'max', // 或者指定数字
        exec_mode: 'cluster',
        env: {
            NODE_ENV: 'development'
        },
        env_production: {
            NODE_ENV: 'production',
            PORT: 3000
        },
        max_memory_restart: '1G',
        error_file: './logs/err.log',
        out_file: './logs/out.log',
        log_file: './logs/combined.log',
        time: true
    }]
};

// ✅ 健康检查
function createHealthCheck() {
    return (req, res) => {
        const healthcheck = {
            uptime: process.uptime(),
            message: 'OK',
            timestamp: Date.now(),
            memory: process.memoryUsage(),
            pid: process.pid
        };
        
        try {
            res.status(200).send(healthcheck);
        } catch (error) {
            healthcheck.message = error;
            res.status(503).send();
        }
    };
}

app.get('/health', createHealthCheck());

性能监控和工具

// ✅ 自定义性能监控
class PerformanceMonitor {
    constructor() {
        this.metrics = {
            requests: 0,
            errors: 0,
            responseTime: [],
            memoryUsage: [],
            cpuUsage: []
        };
        
        this.startMonitoring();
    }
    
    recordRequest(duration, isError = false) {
        this.metrics.requests++;
        this.metrics.responseTime.push(duration);
        
        if (isError) {
            this.metrics.errors++;
        }
        
        // 保持最近1000个请求的数据
        if (this.metrics.responseTime.length > 1000) {
            this.metrics.responseTime.shift();
        }
    }
    
    startMonitoring() {
        setInterval(() => {
            // 记录内存使用
            const memory = process.memoryUsage();
            this.metrics.memoryUsage.push({
                timestamp: Date.now(),
                rss: memory.rss,
                heapUsed: memory.heapUsed
            });
            
            // 保持最近100个记录
            if (this.metrics.memoryUsage.length > 100) {
                this.metrics.memoryUsage.shift();
            }
        }, 5000);
    }
    
    getStats() {
        const responseTime = this.metrics.responseTime;
        const avgResponseTime = responseTime.length > 0 
            ? responseTime.reduce((a, b) => a + b) / responseTime.length 
            : 0;
            
        return {
            totalRequests: this.metrics.requests,
            totalErrors: this.metrics.errors,
            errorRate: this.metrics.requests > 0 
                ? (this.metrics.errors / this.metrics.requests * 100).toFixed(2) + '%'
                : '0%',
            avgResponseTime: avgResponseTime.toFixed(2) + 'ms',
            currentMemory: process.memoryUsage(),
            uptime: process.uptime()
        };
    }
}

const monitor = new PerformanceMonitor();

// 在中间件中使用
app.use((req, res, next) => {
    const start = Date.now();
    
    res.on('finish', () => {
        const duration = Date.now() - start;
        const isError = res.statusCode >= 400;
        monitor.recordRequest(duration, isError);
    });
    
    next();
});

// 性能报告接口
app.get('/metrics', (req, res) => {
    res.json(monitor.getStats());
});

最佳实践总结

✅ 性能优化检查清单:

  • 使用最新版本的Node.js和依赖包
  • 启用HTTP压缩和缓存
  • 实现适当的错误处理和日志记录
  • 使用连接池管理数据库连接
  • 监控内存使用和垃圾回收
  • 避免阻塞事件循环的同步操作
  • 实现健康检查和性能监控
  • 使用集群模式充分利用多核CPU

⚠️ 常见性能陷阱:

  • 同步I/O操作阻塞事件循环
  • 内存泄漏导致应用崩溃
  • 未正确配置数据库连接池
  • 过度使用递归导致栈溢出
  • 未实现适当的缓存策略
  • 忽略错误处理和监控

推荐工具和资源

🛠️ 性能分析工具:

  • Clinic.js - 综合性能诊断工具
  • 0x - 火焰图生成器
  • autocannon - HTTP基准测试工具
  • PM2 - 生产环境进程管理器
  • New Relic / AppDynamics - 应用性能监控
  • Chrome DevTools - Node.js调试和分析

总结

Node.js性能优化是一个系统性工程,需要从内存管理、CPU使用、I/O优化、网络配置等多个维度进行考虑。通过合理的架构设计、代码优化、监控机制和部署策略,可以显著提升应用性能。记住,性能优化应该基于实际的性能分析数据,而不是猜测。始终先测量,再优化,然后验证优化效果。

Next Post Previous Post
No Comment
Add Comment
comment url