Appearance
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);
}
});
性能优化检查清单
部署前性能检查
- 启用压缩 - 使用compress中间件
- 设置缓存头 - 为静态资源设置长期缓存
- 优化数据库查询 - 使用索引和投影
- 实现缓存策略 - 使用Redis或内存缓存
- 并行处理 - 使用Promise.all进行并行操作
- 限制请求大小 - 设置适当的请求体限制
- 使用集群模式 - 利用多核CPU
- 监控性能 - 实施性能监控
- 优化中间件顺序 - 将高频使用中间件放在前面
- 减少响应大小 - 只返回必要的数据
- 连接池配置 - 合理配置数据库连接池
- 内存管理 - 避免内存泄漏
- 异步优化 - 避免阻塞操作
- 路由优化 - 使用高效的路由策略
通过实施这些性能优化策略,可以显著提高Koa应用的响应速度、吞吐量和资源利用率。