Skip to content
On this page

Koa性能优化

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

基础性能优化

使用压缩中间件

bash
npm install koa-compress
javascript
const compress = require('koa-compress');

// 配置压缩中间件
app.use(compress({
  threshold: 2048,  // 大于2KB的响应才压缩
  gzip: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  deflate: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  br: false // 禁用Brotli压缩(如果不需要)
}));

设置正确的缓存头

javascript
// 为静态资源设置长期缓存
app.use(mount('/static', serve(path.join(__dirname, 'public'), {
  maxage: 1000 * 60 * 60 * 24 * 365, // 1年缓存
  gzip: true
})));

// 为API响应设置适当的缓存
app.use(async (ctx, next) => {
  await next();
  
  // 为特定的API端点设置缓存
  if (ctx.path.startsWith('/api/config')) {
    ctx.set('Cache-Control', 'public, max-age=3600'); // 1小时缓存
  }
});

中间件优化

中间件顺序优化

javascript
// 1. 首先是性能相关的中间件
app.use(compress());                    // 压缩
app.use(helmet());                     // 安全头

// 2. 然后是解析中间件
app.use(bodyParser({ json: true, text: true }));

// 3. 接着是日志中间件(在解析后)
app.use(logger());

// 4. 然后是认证中间件
app.use(authentication());

// 5. 最后是路由和业务逻辑
app.use(routes());

条件中间件使用

javascript
// 只在特定路径使用中间件
app.use(async (ctx, next) => {
  if (ctx.path.startsWith('/api/')) {
    // 只对API路径应用此中间件
    await apiMiddleware(ctx, next);
  } else {
    await next();
  }
});

// 延迟加载中间件
const expensiveMiddleware = () => {
  // 延迟初始化复杂中间件
  const middlewareLogic = require('./complex-logic');
  return async (ctx, next) => {
    await middlewareLogic(ctx, next);
  };
};

// 只在需要时加载
if (process.env.ENABLE_COMPLEX_FEATURE) {
  app.use(expensiveMiddleware());
}

数据库优化

连接池配置

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

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

查询优化

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

// 复合索引优化
app.use('/api/search', async (ctx) => {
  const { status, role } = ctx.query;
  
  // 确保在status和role字段上创建了复合索引
  const users = await User.find({ status, role })
    .select('name email') // 选择特定字段
    .limit(100);
  
  ctx.body = users;
});

缓存策略

内存缓存

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

// 缓存中间件
const cacheMiddleware = (duration = 600) => {
  return async (ctx, next) => {
    const key = `__cache__${ctx.method}_${ctx.url}`;
    const cachedBody = cache.get(key);
    
    if (cachedBody) {
      ctx.body = cachedBody;
      ctx.status = 200;
      return;
    }
    
    await next();
    
    // 只缓存成功的GET请求
    if (ctx.method === 'GET' && ctx.status >= 200 && ctx.status < 300) {
      cache.set(key, ctx.body, duration);
    }
  };
};

// 使用缓存中间件
app.use('/api/expensive-operation', cacheMiddleware(300), async (ctx) => {
  // 耗时操作
  ctx.body = await performExpensiveOperation();
});

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.use('/api/products/:id', async (ctx) => {
  ctx.body = await getCachedData(
    `product:${ctx.params.id}`,
    () => Product.findById(ctx.params.id),
    600 // 10分钟缓存
  );
});

异步操作优化

并行处理

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

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

流处理大文件

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

// 流处理大文件
app.use('/api/upload/csv', async (ctx) => {
  const results = [];
  
  return new Promise((resolve, reject) => {
    fs.createReadStream(ctx.request.files.file.path)
      .pipe(csv())
      .on('data', (data) => {
        results.push(data);
      })
      .on('end', () => {
        // 处理结果
        processCSVData(results);
        fs.unlinkSync(ctx.request.files.file.path); // 删除临时文件
        ctx.body = { message: 'CSV processed', count: results.length };
        resolve();
      })
      .on('error', (error) => {
        reject(error);
      });
  });
});

路由优化

路由参数预处理

javascript
const Router = require('@koa/router');
const router = new Router();

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

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

路由拆分

javascript
// routes/users.js
const Router = require('@koa/router');
const router = new 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.use('/api/data/:id', async (ctx) => {
  const largeDataSet = getLargeDataSet(); // 每次请求都创建大数据集
  
  const handler = () => {
    // 大数据集被闭包引用,可能导致内存泄漏
    processDataSet(largeDataSet);
  };
  
  handler();
  ctx.body = { status: 'done' };
});

// 更好的做法
app.use('/api/data/:id', async (ctx) => {
  ctx.body = await getOptimizedData(ctx.params.id);
});

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

app.use('/api/user-data/:id', async (ctx) => {
  const user = getUserById(ctx.params.id);
  let userData = userDataCache.get(user);
  
  if (!userData) {
    userData = processUserData(user);
    userDataCache.set(user, userData);
  }
  
  ctx.body = userData;
});

响应优化

流式响应

javascript
// 流式响应大数据
app.use('/api/large-data', async (ctx) => {
  ctx.set('Content-Type', 'application/json');
  ctx.set('Transfer-Encoding', 'chunked');
  
  ctx.body = 'data:';
  
  const stream = getDataStream();
  
  stream.on('data', (chunk) => {
    ctx.res.write(JSON.stringify(chunk) + '\n');
  });
  
  stream.on('end', () => {
    ctx.res.end();
  });
});

响应压缩

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

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

监控和性能分析

性能监控中间件

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

使用APM工具

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

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

应用层优化

预热和初始化优化

javascript
// 应用启动时预热
const prewarm = async () => {
  // 预加载常用数据
  await Promise.all([
    loadCommonConfig(),
    initializeCache(),
    warmUpDatabaseConnections()
  ]);
  
  console.log('Application prewarming completed');
};

// 在应用启动后执行预热
app.listen(PORT, async () => {
  await prewarm();
  console.log(`Server running on port ${PORT}`);
});

连接复用

javascript
// 数据库连接复用
const dbPool = require('./db-pool');

app.use(async (ctx, next) => {
  // 为请求设置数据库连接
  ctx.db = dbPool.getConnection();
  
  try {
    await next();
  } finally {
    // 释放连接
    dbPool.releaseConnection(ctx.db);
  }
});

性能优化检查清单

部署前性能检查

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

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