mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
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:
@@ -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小时预检缓存
|
||||
|
||||
Reference in New Issue
Block a user