Skip to content
On this page

Express性能优化

性能优化是确保Express应用能够高效处理请求、响应快速、资源利用合理的关键。本指南详细介绍各种性能优化策略和技术。

基础性能优化

使用gzip压缩

javascript
const compression = require('compression');

// 启用gzip压缩
app.use(compression({
  // 只压缩大于1KB的响应
  threshold: 1024,
  // 设置压缩级别 (0-9, 9是最高压缩)
  level: 6,
  // 设置内存级别 (1-9, 影响速度和压缩比)
  memLevel: 8
}));

设置正确的缓存头

javascript
// 为静态资源设置长期缓存
app.use('/static', express.static('public', {
  maxAge: '1y',           // 1年缓存
  etag: true,             // 启用ETag
  lastModified: true      // 启用Last-Modified
}));

// 为API响应设置适当的缓存
app.get('/api/config', (req, res) => {
  res.set('Cache-Control', 'public, max-age=3600'); // 缓存1小时
  res.json(getConfig());
});

中间件优化

中间件顺序优化

javascript
// 1. 首先是安全中间件
app.use(helmet());

// 2. 然后是解析中间件
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// 3. 接着是日志中间件
if (process.env.NODE_ENV === 'development') {
  app.use(morgan('dev'));
}

// 4. 然后是静态文件服务
app.use('/static', express.static('public', { maxAge: '1y' }));

// 5. 最后是路由和应用逻辑
app.use('/api', apiRoutes);

条件中间件使用

javascript
// 只在特定路径使用中间件
app.use('/api', someHeavyMiddleware);

// 根据环境条件使用中间件
if (process.env.NODE_ENV === 'development') {
  app.use('/api', devMiddleware);
}

// 根据请求类型使用中间件
app.use((req, res, next) => {
  if (req.path.startsWith('/api/')) {
    // API特定中间件
    req.isApiRequest = true;
  }
  next();
});

数据库优化

连接池配置

javascript
// 数据库连接池优化(以MongoDB为例)
const mongoose = require('mongoose');

mongoose.connect(process.env.MONGODB_URI, {
  maxPoolSize: 10,        // 连接池最大连接数
  minPoolSize: 5,         // 连接池最小连接数
  maxIdleTimeMS: 30000,   // 空闲连接最大时间
  serverSelectionTimeoutMS: 5000, // 服务器选择超时
  socketTimeoutMS: 45000, // 套接字超时
});

查询优化

javascript
// 使用索引优化查询
app.get('/users', async (req, res) => {
  const { page = 1, limit = 10, sort = 'createdAt' } = req.query;
  
  // 使用投影只返回需要的字段
  const users = await User.find({}, 'name email createdAt')
    .skip((page - 1) * limit)
    .limit(parseInt(limit))
    .sort(sort)
    .lean(); // 使用lean()返回纯JavaScript对象,提高性能
  
  res.json(users);
});

// 复合索引优化
app.get('/users/search', async (req, res) => {
  const { status, role } = req.query;
  
  // 确保在status和role字段上创建了复合索引
  const users = await User.find({ status, role })
    .select('name email')
    .limit(100);
  
  res.json(users);
});

缓存策略

内存缓存

javascript
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10分钟默认TTL

// 缓存中间件
const cacheMiddleware = (duration = 600) => {
  return (req, res, next) => {
    const key = '__cache__' + req.originalUrl || req.url;
    const cachedBody = cache.get(key);
    
    if (cachedBody) {
      res.send(cachedBody);
      return;
    }
    
    // 拦截res.send并缓存结果
    res.sendResponse = res.send;
    res.send = (body) => {
      cache.set(key, body, duration);
      res.sendResponse(body);
    };
    
    next();
  };
};

// 使用缓存中间件
app.get('/expensive-operation', cacheMiddleware(300), async (req, res) => {
  // 耗时操作
  const result = await performExpensiveOperation();
  res.json(result);
});

Redis缓存

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

// Redis缓存函数
const getCachedData = async (key, fetchFunction, ttl = 300) => {
  try {
    const cached = await client.get(key);
    if (cached) {
      return JSON.parse(cached);
    }
    
    const data = await fetchFunction();
    await client.setex(key, ttl, JSON.stringify(data));
    return data;
  } catch (error) {
    console.error('Cache error:', error);
    return await fetchFunction(); // 回退到直接获取
  }
};

// 使用Redis缓存
app.get('/products/:id', async (req, res) => {
  const product = await getCachedData(
    `product:${req.params.id}`,
    () => Product.findById(req.params.id),
    600 // 10分钟缓存
  );
  
  res.json(product);
});

异步操作优化

并行处理

javascript
// 避免连续的异步操作
// 不好的做法
app.get('/dashboard', async (req, res) => {
  const user = await User.findById(req.user.id);
  const orders = await Order.find({ userId: req.user.id });
  const notifications = await Notification.find({ userId: req.user.id });
  
  res.json({ user, orders, notifications });
});

// 更好的做法 - 并行处理
app.get('/dashboard', async (req, res) => {
  const [user, orders, notifications] = await Promise.all([
    User.findById(req.user.id),
    Order.find({ userId: req.user.id }),
    Notification.find({ userId: req.user.id })
  ]);
  
  res.json({ user, orders, notifications });
});

流处理大文件

javascript
const fs = require('fs');
const csv = require('csv-parser');

// 流处理大文件
app.post('/upload/csv', upload.single('file'), (req, res) => {
  const results = [];
  
  fs.createReadStream(req.file.path)
    .pipe(csv())
    .on('data', (data) => {
      results.push(data);
    })
    .on('end', () => {
      // 处理结果
      processCSVData(results);
      fs.unlinkSync(req.file.path); // 删除临时文件
      res.json({ message: 'CSV processed', count: results.length });
    });
});

路由优化

路由参数预处理

javascript
// 使用app.param预处理路由参数
app.param('userId', async (req, res, next, id) => {
  try {
    // 验证和缓存用户信息
    const user = await User.findById(id);
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
    req.userDoc = user;
    next();
  } catch (error) {
    next(error);
  }
});

// 路由中直接使用预处理的数据
app.get('/users/:userId/profile', (req, res) => {
  res.json(req.userDoc); // 使用预加载的用户数据
});

路由拆分

javascript
// routes/users.js
const express = require('express');
const router = express.Router();

// 批量中间件应用
router.use(authenticate); // 为所有用户路由应用认证

router.get('/', getUsers);
router.get('/:id', getUser);
router.post('/', validateUser, createUser);
router.put('/:id', validateUser, updateUser);
router.delete('/:id', deleteUser);

module.exports = router;

// app.js
app.use('/api/users', require('./routes/users'));

服务器优化

集群模式

javascript
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);
  
  // 创建工作进程
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork(); // 重启工作进程
  });
} else {
  // 工作进程
  app.listen(3000, () => {
    console.log(`Worker ${process.pid} started`);
  });
}

连接管理

javascript
// 服务器连接管理
const server = app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

// 优雅关闭
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully');
  server.close(() => {
    console.log('Process terminated');
  });
  
  // 设置超时强制退出
  setTimeout(() => {
    console.log('Could not close connections in time, forcefully shutting down');
    process.exit(1);
  }, 10000);
});

内存优化

避免内存泄漏

javascript
// 避免在闭包中存储大量数据
// 不好的做法
app.get('/data/:id', (req, res) => {
  const largeDataSet = getLargeDataSet(); // 每次请求都创建大数据集
  
  const handler = () => {
    // 大数据集被闭包引用,可能导致内存泄漏
    processDataSet(largeDataSet);
  };
  
  handler();
  res.json({ status: 'done' });
});

// 更好的做法
app.get('/data/:id', async (req, res) => {
  const data = await getOptimizedData(req.params.id);
  res.json(data);
});

// 使用WeakMap避免内存泄漏
const userDataCache = new WeakMap();

app.get('/user-data/:id', (req, res) => {
  const user = getUserById(req.params.id);
  let userData = userDataCache.get(user);
  
  if (!userData) {
    userData = processUserData(user);
    userDataCache.set(user, userData);
  }
  
  res.json(userData);
});

响应优化

流式响应

javascript
// 流式响应大数据
app.get('/large-data', (req, res) => {
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('Transfer-Encoding', 'chunked');
  
  res.write('{"data":[');
  
  let first = true;
  const stream = getDataStream();
  
  stream.on('data', (chunk) => {
    if (!first) res.write(',');
    first = false;
    res.write(JSON.stringify(chunk));
  });
  
  stream.on('end', () => {
    res.write(']}');
    res.end();
  });
});

响应压缩

javascript
// 根据客户端支持选择压缩算法
const accepts = require('accepts');

app.get('/api/data', (req, res) => {
  const accept = accepts(req);
  const data = getLargeDataSet();
  
  // 检查客户端是否支持压缩
  if (accept.encoding(['gzip', 'deflate'])) {
    res.setHeader('Content-Encoding', 'gzip');
    // 发送压缩数据
  }
  
  res.json(data);
});

监控和性能分析

性能监控中间件

javascript
// 请求性能监控
app.use((req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    
    // 记录慢请求
    if (duration > 1000) { // 超过1秒的请求
      console.log(`Slow request: ${req.method} ${req.path} - ${duration}ms`);
    }
    
    // 可以发送到监控服务
    performanceMetrics.recordRequest({
      method: req.method,
      path: req.path,
      duration,
      statusCode: res.statusCode
    });
  });
  
  next();
});

使用APM工具

javascript
// 使用New Relic进行应用性能监控
const newrelic = require('newrelic');

// 或者使用其他APM工具如DataDog, AppDynamics等

性能优化检查清单

部署前性能检查

  1. 启用压缩 - 使用compression中间件
  2. 设置缓存头 - 为静态资源设置长期缓存
  3. 优化数据库查询 - 使用索引和投影
  4. 实现缓存策略 - 使用Redis或内存缓存
  5. 并行处理 - 使用Promise.all进行并行操作
  6. 限制请求大小 - 设置适当的请求体限制
  7. 使用集群模式 - 利用多核CPU
  8. 监控性能 - 实施性能监控
  9. 优化中间件顺序 - 将高频使用中间件放在前面
  10. 减少响应大小 - 只返回必要的数据

通过实施这些性能优化策略,可以显著提高Express应用的响应速度、吞吐量和资源利用率。