feat: 优化并发控制和移除冗余限制功能

主要改进:
1. 改进并发控制机制
   - 使用 once 代替 on 避免重复监听
   - 监听多个事件确保可靠性(close、finish)
   - 支持客户端断开时立即释放并发槽位

2. 支持非流式请求的客户端断开处理
   - 客户端断开时立即中断上游请求
   - 避免资源浪费和不必要的 API 调用

3. 移除 requestLimit(请求数限制)功能
   - 移除配置和验证逻辑
   - 保留请求统计用于监控分析

4. 移除速率限制(Rate Limit)功能
   - 移除 RATE_LIMIT_* 配置
   - 简化中间件逻辑
   - 避免与并发控制重复

现在系统仅保留:
- Token 使用量限制
- 并发数限制(更精确的资源控制)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-16 14:40:37 +08:00
parent f9bc2ddb23
commit 567e3b25aa
9 changed files with 62 additions and 108 deletions

View File

@@ -42,24 +42,6 @@ const authenticateApiKey = async (req, res, next) => {
});
}
// 检查速率限制(优化:只在验证成功后检查)
const rateLimitResult = await apiKeyService.checkRateLimit(validation.keyData.id);
if (!rateLimitResult.allowed) {
logger.security(`🚦 Rate limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name})`);
return res.status(429).json({
error: 'Rate limit exceeded',
message: `Too many requests. Limit: ${rateLimitResult.limit} requests per minute`,
resetTime: rateLimitResult.resetTime,
retryAfter: rateLimitResult.resetTime
});
}
// 设置标准速率限制响应头
res.setHeader('X-RateLimit-Limit', rateLimitResult.limit);
res.setHeader('X-RateLimit-Remaining', Math.max(0, rateLimitResult.limit - rateLimitResult.current));
res.setHeader('X-RateLimit-Reset', rateLimitResult.resetTime);
res.setHeader('X-RateLimit-Policy', `${rateLimitResult.limit};w=60`);
// 检查并发限制
const concurrencyLimit = validation.keyData.concurrencyLimit || 0;
@@ -94,21 +76,29 @@ const authenticateApiKey = async (req, res, next) => {
}
};
// 监听多个事件以确保在各种情况下都能正确减少计数
res.on('finish', decrementConcurrency);
res.on('error', decrementConcurrency);
req.on('close', () => {
// 监听最可靠的事件(避免重复监听)
// res.on('close') 是最可靠的,会在连接关闭时触发
res.once('close', () => {
logger.api(`🔌 Response closed for key: ${validation.keyData.id} (${validation.keyData.name})`);
decrementConcurrency();
});
// req.on('close') 作为备用,处理请求端断开
req.once('close', () => {
logger.api(`🔌 Request closed for key: ${validation.keyData.id} (${validation.keyData.name})`);
decrementConcurrency();
});
req.on('aborted', () => {
logger.api(`⚠️ Request aborted for key: ${validation.keyData.id} (${validation.keyData.name})`);
// res.on('finish') 处理正常完成的情况
res.once('finish', () => {
logger.api(`✅ Response finished for key: ${validation.keyData.id} (${validation.keyData.name})`);
decrementConcurrency();
});
// 存储并发信息到请求对象,便于后续处理
req.concurrencyInfo = {
apiKeyId: validation.keyData.id,
apiKeyName: validation.keyData.name,
decrementConcurrency
};
}
@@ -118,7 +108,6 @@ const authenticateApiKey = async (req, res, next) => {
id: validation.keyData.id,
name: validation.keyData.name,
tokenLimit: validation.keyData.tokenLimit,
requestLimit: validation.keyData.requestLimit,
claudeAccountId: validation.keyData.claudeAccountId,
concurrencyLimit: validation.keyData.concurrencyLimit
};
@@ -272,10 +261,8 @@ const corsMiddleware = (req, res, next) => {
].join(', '));
res.header('Access-Control-Expose-Headers', [
'X-RateLimit-Limit',
'X-RateLimit-Remaining',
'X-RateLimit-Reset',
'X-RateLimit-Policy'
'X-Request-ID',
'Content-Type'
].join(', '));
res.header('Access-Control-Max-Age', '86400'); // 24小时预检缓存