返回博客列表
后端开发

Node.js 性能优化实战

2025-09-08
12 分钟阅读
Node.js性能优化后端
Node.js 性能优化实战

Node.js 性能优化实战

Node.js 作为服务端 JavaScript 运行时,在构建高性能应用方面有着独特的优势。本文将分享一些实用的性能优化技巧和最佳实践。

性能监控基础

1. 使用内置性能监控

const { performance, PerformanceObserver } = require('perf_hooks')

// 监控函数执行时间
function measureTime(fn) {
  const start = performance.now()
  const result = fn()
  const end = performance.now()
  console.log(`执行时间: ${end - start} 毫秒`)
  return result
}

// 监控内存使用
function logMemoryUsage() {
  const used = process.memoryUsage()
  console.log('内存使用情况:')
  for (let key in used) {
    console.log(`${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`)
  }
}

2. 使用第三方监控工具

// 使用 clinic.js 进行性能分析
const clinic = require('@nearform/clinic')

// 启动性能分析
clinic.doctor({
  collectOnFailure: true,
  dest: './clinic-results'
})

内存优化

1. 避免内存泄漏

// 错误示例:未清理的定时器
function badExample() {
  setInterval(() => {
    console.log('定时器运行中...')
  }, 1000)
  // 没有清理定时器,会导致内存泄漏
}

// 正确示例:清理定时器
function goodExample() {
  const intervalId = setInterval(() => {
    console.log('定时器运行中...')
  }, 1000)
  
  // 在适当的时候清理
  setTimeout(() => {
    clearInterval(intervalId)
  }, 5000)
}

2. 使用对象池

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(createFn())
    }
  }
  
  acquire() {
    if (this.pool.length > 0) {
      return this.pool.pop()
    }
    return this.createFn()
  }
  
  release(obj) {
    this.resetFn(obj)
    this.pool.push(obj)
  }
}

// 使用示例
const userPool = new ObjectPool(
  () => ({ id: null, name: '', email: '' }),
  (user) => { user.id = null; user.name = ''; user.email = '' }
)

异步处理优化

1. 使用 Promise.all 并行处理

// 错误示例:串行处理
async function badExample() {
  const user1 = await fetchUser(1)
  const user2 = await fetchUser(2)
  const user3 = await fetchUser(3)
  return [user1, user2, user3]
}

// 正确示例:并行处理
async function goodExample() {
  const [user1, user2, user3] = await Promise.all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3)
  ])
  return [user1, user2, user3]
}

2. 使用流处理大文件

const fs = require('fs')
const { Transform } = require('stream')

// 创建转换流
const processData = new Transform({
  objectMode: true,
  transform(chunk, encoding, callback) {
    // 处理数据块
    const processed = processChunk(chunk)
    callback(null, processed)
  }
})

// 使用流处理大文件
function processLargeFile(inputPath, outputPath) {
  return new Promise((resolve, reject) => {
    const readStream = fs.createReadStream(inputPath)
    const writeStream = fs.createWriteStream(outputPath)
    
    readStream
      .pipe(processData)
      .pipe(writeStream)
      .on('finish', resolve)
      .on('error', reject)
  })
}

数据库优化

1. 连接池管理

const mysql = require('mysql2/promise')

// 创建连接池
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'mydb',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
  acquireTimeout: 60000,
  timeout: 60000
})

// 使用连接池
async function getUsers() {
  const connection = await pool.getConnection()
  try {
    const [rows] = await connection.execute('SELECT * FROM users')
    return rows
  } finally {
    connection.release()
  }
}

2. 查询优化

// 使用索引优化查询
async function getUsersByEmail(email) {
  const query = 'SELECT * FROM users WHERE email = ?'
  const [rows] = await pool.execute(query, [email])
  return rows
}

// 使用分页避免大量数据查询
async function getUsersPaginated(page = 1, limit = 10) {
  const offset = (page - 1) * limit
  const query = 'SELECT * FROM users LIMIT ? OFFSET ?'
  const [rows] = await pool.execute(query, [limit, offset])
  return rows
}

缓存策略

1. 内存缓存

const NodeCache = require('node-cache')

// 创建缓存实例
const cache = new NodeCache({ 
  stdTTL: 600, // 默认过期时间 10 分钟
  checkperiod: 120 // 检查过期时间 2 分钟
})

// 缓存函数
function withCache(key, ttl = 600) {
  return function(target, propertyName, descriptor) {
    const method = descriptor.value
    
    descriptor.value = async function(...args) {
      const cacheKey = `${key}:${JSON.stringify(args)}`
      
      // 尝试从缓存获取
      let result = cache.get(cacheKey)
      if (result) {
        console.log('从缓存获取数据')
        return result
      }
      
      // 执行原函数
      result = await method.apply(this, args)
      
      // 存储到缓存
      cache.set(cacheKey, result, ttl)
      console.log('数据已缓存')
      
      return result
    }
  }
}

// 使用示例
class UserService {
  @withCache('user', 300)
  async getUserById(id) {
    // 模拟数据库查询
    return await db.query('SELECT * FROM users WHERE id = ?', [id])
  }
}

2. Redis 缓存

const redis = require('redis')
const client = redis.createClient()

// 缓存装饰器
function redisCache(key, ttl = 600) {
  return function(target, propertyName, descriptor) {
    const method = descriptor.value
    
    descriptor.value = async function(...args) {
      const cacheKey = `${key}:${JSON.stringify(args)}`
      
      try {
        // 尝试从 Redis 获取
        const cached = await client.get(cacheKey)
        if (cached) {
          console.log('从 Redis 缓存获取数据')
          return JSON.parse(cached)
        }
        
        // 执行原函数
        const result = await method.apply(this, args)
        
        // 存储到 Redis
        await client.setex(cacheKey, ttl, JSON.stringify(result))
        console.log('数据已存储到 Redis')
        
        return result
      } catch (error) {
        console.error('缓存错误:', error)
        return await method.apply(this, args)
      }
    }
  }
}

代码优化技巧

1. 避免阻塞操作

// 错误示例:同步文件操作
function badExample() {
  const data = fs.readFileSync('large-file.txt', 'utf8')
  return processData(data)
}

// 正确示例:异步文件操作
async function goodExample() {
  const data = await fs.promises.readFile('large-file.txt', 'utf8')
  return processData(data)
}

2. 使用 Worker Threads

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads')

// 主线程
if (isMainThread) {
  function runWorker(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 停止,退出码: ${code}`))
        }
      })
    })
  }
  
  // 使用 Worker
  async function processLargeData(data) {
    const result = await runWorker(data)
    return result
  }
} else {
  // Worker 线程
  const result = heavyComputation(workerData)
  parentPort.postMessage(result)
}

应用架构优化

1. 微服务架构

// 服务发现
const consul = require('consul')

class ServiceRegistry {
  constructor() {
    this.consul = consul()
  }
  
  async registerService(service) {
    await this.consul.agent.service.register({
      name: service.name,
      address: service.address,
      port: service.port,
      check: {
        http: `http://${service.address}:${service.port}/health`,
        interval: '10s'
      }
    })
  }
  
  async discoverService(serviceName) {
    const services = await this.consul.health.service({
      service: serviceName,
      passing: true
    })
    return services[0]
  }
}

2. 负载均衡

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} 已退出`)
    cluster.fork() // 重启工作进程
  })
} else {
  // 工作进程
  const express = require('express')
  const app = express()
  
  app.get('/', (req, res) => {
    res.json({ 
      message: 'Hello World!', 
      pid: process.pid 
    })
  })
  
  app.listen(3000, () => {
    console.log(`工作进程 ${process.pid} 已启动`)
  })
}

监控和调试

1. 性能指标收集

const prometheus = require('prom-client')

// 创建指标
const httpRequestDuration = new prometheus.Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP 请求持续时间',
  labelNames: ['method', 'route', 'status_code']
})

const httpRequestTotal = new prometheus.Counter({
  name: 'http_requests_total',
  help: 'HTTP 请求总数',
  labelNames: ['method', 'route', 'status_code']
})

// 中间件
function metricsMiddleware(req, res, next) {
  const start = Date.now()
  
  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000
    const labels = {
      method: req.method,
      route: req.route?.path || req.path,
      status_code: res.statusCode
    }
    
    httpRequestDuration.observe(labels, duration)
    httpRequestTotal.inc(labels)
  })
  
  next()
}

2. 健康检查

const express = require('express')
const app = express()

// 健康检查端点
app.get('/health', async (req, res) => {
  const health = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    version: process.version
  }
  
  // 检查数据库连接
  try {
    await checkDatabaseConnection()
    health.database = 'ok'
  } catch (error) {
    health.database = 'error'
    health.status = 'error'
  }
  
  const statusCode = health.status === 'ok' ? 200 : 503
  res.status(statusCode).json(health)
})

async function checkDatabaseConnection() {
  // 实现数据库连接检查
  return true
}

总结

Node.js 性能优化是一个持续的过程,需要从多个方面入手:

  1. 监控和测量:建立完善的性能监控体系
  2. 内存管理:避免内存泄漏,合理使用内存
  3. 异步处理:充分利用 Node.js 的异步特性
  4. 数据库优化:使用连接池,优化查询
  5. 缓存策略:合理使用各种缓存技术
  6. 代码优化:避免阻塞操作,使用 Worker Threads
  7. 架构设计:采用微服务和负载均衡

记住,性能优化不是一蹴而就的,需要根据实际应用场景和性能瓶颈进行有针对性的优化。