mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
fix: 优化限流机制,使限流时间与Claude会话窗口保持一致
- 限流结束时间现在基于5小时会话窗口而非硬编码1小时 - 新增 rateLimitEndAt 字段追踪限流结束时间 - 自动解除限流基于会话窗口结束时间 - 保持向后兼容,支持旧数据格式 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -739,10 +739,28 @@ class ClaudeAccountService {
|
|||||||
throw new Error('Account not found');
|
throw new Error('Account not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取或创建会话窗口
|
||||||
|
const updatedAccountData = await this.updateSessionWindow(accountId, accountData);
|
||||||
|
|
||||||
// 设置限流状态和时间
|
// 设置限流状态和时间
|
||||||
accountData.rateLimitedAt = new Date().toISOString();
|
updatedAccountData.rateLimitedAt = new Date().toISOString();
|
||||||
accountData.rateLimitStatus = 'limited';
|
updatedAccountData.rateLimitStatus = 'limited';
|
||||||
await redis.setClaudeAccount(accountId, accountData);
|
|
||||||
|
// 限流结束时间 = 会话窗口结束时间
|
||||||
|
if (updatedAccountData.sessionWindowEnd) {
|
||||||
|
updatedAccountData.rateLimitEndAt = updatedAccountData.sessionWindowEnd;
|
||||||
|
const windowEnd = new Date(updatedAccountData.sessionWindowEnd);
|
||||||
|
const now = new Date();
|
||||||
|
const minutesUntilEnd = Math.ceil((windowEnd - now) / (1000 * 60));
|
||||||
|
logger.warn(`🚫 Account marked as rate limited until session window ends: ${accountData.name} (${accountId}) - ${minutesUntilEnd} minutes remaining`);
|
||||||
|
} else {
|
||||||
|
// 如果没有会话窗口,使用默认1小时(兼容旧逻辑)
|
||||||
|
const oneHourLater = new Date(Date.now() + 60 * 60 * 1000);
|
||||||
|
updatedAccountData.rateLimitEndAt = oneHourLater.toISOString();
|
||||||
|
logger.warn(`🚫 Account marked as rate limited (1 hour default): ${accountData.name} (${accountId})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await redis.setClaudeAccount(accountId, updatedAccountData);
|
||||||
|
|
||||||
// 如果有会话哈希,删除粘性会话映射
|
// 如果有会话哈希,删除粘性会话映射
|
||||||
if (sessionHash) {
|
if (sessionHash) {
|
||||||
@@ -750,7 +768,6 @@ class ClaudeAccountService {
|
|||||||
logger.info(`🗑️ Deleted sticky session mapping for rate limited account: ${accountId}`);
|
logger.info(`🗑️ Deleted sticky session mapping for rate limited account: ${accountId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.warn(`🚫 Account marked as rate limited: ${accountData.name} (${accountId})`);
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`❌ Failed to mark account as rate limited: ${accountId}`, error);
|
logger.error(`❌ Failed to mark account as rate limited: ${accountId}`, error);
|
||||||
@@ -769,6 +786,7 @@ class ClaudeAccountService {
|
|||||||
// 清除限流状态
|
// 清除限流状态
|
||||||
delete accountData.rateLimitedAt;
|
delete accountData.rateLimitedAt;
|
||||||
delete accountData.rateLimitStatus;
|
delete accountData.rateLimitStatus;
|
||||||
|
delete accountData.rateLimitEndAt; // 清除限流结束时间
|
||||||
await redis.setClaudeAccount(accountId, accountData);
|
await redis.setClaudeAccount(accountId, accountData);
|
||||||
|
|
||||||
logger.success(`✅ Rate limit removed for account: ${accountData.name} (${accountId})`);
|
logger.success(`✅ Rate limit removed for account: ${accountData.name} (${accountId})`);
|
||||||
@@ -789,8 +807,22 @@ class ClaudeAccountService {
|
|||||||
|
|
||||||
// 检查是否有限流状态
|
// 检查是否有限流状态
|
||||||
if (accountData.rateLimitStatus === 'limited' && accountData.rateLimitedAt) {
|
if (accountData.rateLimitStatus === 'limited' && accountData.rateLimitedAt) {
|
||||||
const rateLimitedAt = new Date(accountData.rateLimitedAt);
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
|
// 优先使用 rateLimitEndAt(基于会话窗口)
|
||||||
|
if (accountData.rateLimitEndAt) {
|
||||||
|
const rateLimitEndAt = new Date(accountData.rateLimitEndAt);
|
||||||
|
|
||||||
|
// 如果当前时间超过限流结束时间,自动解除
|
||||||
|
if (now >= rateLimitEndAt) {
|
||||||
|
await this.removeAccountRateLimit(accountId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// 兼容旧数据:使用1小时限流
|
||||||
|
const rateLimitedAt = new Date(accountData.rateLimitedAt);
|
||||||
const hoursSinceRateLimit = (now - rateLimitedAt) / (1000 * 60 * 60);
|
const hoursSinceRateLimit = (now - rateLimitedAt) / (1000 * 60 * 60);
|
||||||
|
|
||||||
// 如果限流超过1小时,自动解除
|
// 如果限流超过1小时,自动解除
|
||||||
@@ -801,6 +833,7 @@ class ClaudeAccountService {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -821,13 +854,29 @@ class ClaudeAccountService {
|
|||||||
const rateLimitedAt = new Date(accountData.rateLimitedAt);
|
const rateLimitedAt = new Date(accountData.rateLimitedAt);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const minutesSinceRateLimit = Math.floor((now - rateLimitedAt) / (1000 * 60));
|
const minutesSinceRateLimit = Math.floor((now - rateLimitedAt) / (1000 * 60));
|
||||||
const minutesRemaining = Math.max(0, 60 - minutesSinceRateLimit);
|
|
||||||
|
let minutesRemaining;
|
||||||
|
let rateLimitEndAt;
|
||||||
|
|
||||||
|
// 优先使用 rateLimitEndAt(基于会话窗口)
|
||||||
|
if (accountData.rateLimitEndAt) {
|
||||||
|
rateLimitEndAt = accountData.rateLimitEndAt;
|
||||||
|
const endTime = new Date(accountData.rateLimitEndAt);
|
||||||
|
minutesRemaining = Math.max(0, Math.ceil((endTime - now) / (1000 * 60)));
|
||||||
|
} else {
|
||||||
|
// 兼容旧数据:使用1小时限流
|
||||||
|
minutesRemaining = Math.max(0, 60 - minutesSinceRateLimit);
|
||||||
|
// 计算预期的结束时间
|
||||||
|
const endTime = new Date(rateLimitedAt.getTime() + 60 * 60 * 1000);
|
||||||
|
rateLimitEndAt = endTime.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isRateLimited: minutesRemaining > 0,
|
isRateLimited: minutesRemaining > 0,
|
||||||
rateLimitedAt: accountData.rateLimitedAt,
|
rateLimitedAt: accountData.rateLimitedAt,
|
||||||
minutesSinceRateLimit,
|
minutesSinceRateLimit,
|
||||||
minutesRemaining
|
minutesRemaining,
|
||||||
|
rateLimitEndAt // 新增:限流结束时间
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,7 +884,8 @@ class ClaudeAccountService {
|
|||||||
isRateLimited: false,
|
isRateLimited: false,
|
||||||
rateLimitedAt: null,
|
rateLimitedAt: null,
|
||||||
minutesSinceRateLimit: 0,
|
minutesSinceRateLimit: 0,
|
||||||
minutesRemaining: 0
|
minutesRemaining: 0,
|
||||||
|
rateLimitEndAt: null
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`❌ Failed to get rate limit info for account: ${accountId}`, error);
|
logger.error(`❌ Failed to get rate limit info for account: ${accountId}`, error);
|
||||||
|
|||||||
Reference in New Issue
Block a user