Spaces:
Running
Running
github-actions[bot]
Sync from GitHub Viciy2023/Qwen2API-A@ae093476e9bc5b0a599620b5925df3a20057038e
f120063 | const { logger } = require('./logger') | |
| /** | |
| * 账户轮询管理器 | |
| * 负责账户的轮询选择和负载均衡 | |
| */ | |
| class AccountRotator { | |
| constructor() { | |
| this.accounts = [] | |
| this.currentIndex = 0 | |
| this.lastUsedTimes = new Map() // 记录每个账户的最后使用时间 | |
| this.failureCounts = new Map() // 记录每个账户的失败次数 | |
| this.maxFailures = 3 // 最大失败次数 | |
| this.cooldownPeriod = 5 * 60 * 1000 // 5分钟冷却期 | |
| } | |
| /** | |
| * 设置账户列表 | |
| * @param {Array} accounts - 账户列表 | |
| */ | |
| setAccounts(accounts) { | |
| if (!Array.isArray(accounts)) { | |
| logger.error('账户列表必须是数组', 'ACCOUNT') | |
| throw new Error('账户列表必须是数组') | |
| } | |
| this.accounts = [...accounts] | |
| this.currentIndex = 0 | |
| // 清理不存在账户的记录 | |
| this._cleanupRecords() | |
| } | |
| /** | |
| * 获取下一个可用的账户令牌 | |
| * @returns {string|null} 账户令牌或null | |
| */ | |
| getNextToken() { | |
| if (this.accounts.length === 0) { | |
| logger.error('没有可用的账户', 'ACCOUNT') | |
| return null | |
| } | |
| const availableAccounts = this._getAvailableAccounts() | |
| if (availableAccounts.length === 0) { | |
| logger.warn('所有账户都不可用,使用轮询策略', 'ACCOUNT') | |
| return this._getTokenByRoundRobin() | |
| } | |
| // 从可用账户中选择最少使用的 | |
| const selectedAccount = this._selectLeastUsedAccount(availableAccounts) | |
| this._recordUsage(selectedAccount.email) | |
| return selectedAccount.token | |
| } | |
| /** | |
| * 获取指定邮箱的账户令牌 | |
| * @param {string} email - 邮箱地址 | |
| * @returns {string|null} 账户令牌或null | |
| */ | |
| getTokenByEmail(email) { | |
| const account = this.accounts.find(acc => acc.email === email) | |
| if (!account) { | |
| logger.error(`未找到邮箱为 ${email} 的账户`, 'ACCOUNT') | |
| return null | |
| } | |
| if (!this._isAccountAvailable(account)) { | |
| logger.warn(`账户 ${email} 当前不可用`, 'ACCOUNT') | |
| return null | |
| } | |
| this._recordUsage(email) | |
| return account.token | |
| } | |
| /** | |
| * 记录账户使用失败 | |
| * @param {string} email - 邮箱地址 | |
| */ | |
| recordFailure(email) { | |
| const currentFailures = this.failureCounts.get(email) || 0 | |
| this.failureCounts.set(email, currentFailures + 1) | |
| if (currentFailures + 1 >= this.maxFailures) { | |
| logger.warn(`账户 ${email} 失败次数达到上限,将进入冷却期`, 'ACCOUNT') | |
| } | |
| } | |
| /** | |
| * 重置账户失败计数 | |
| * @param {string} email - 邮箱地址 | |
| */ | |
| resetFailures(email) { | |
| this.failureCounts.delete(email) | |
| } | |
| /** | |
| * 获取账户统计信息 | |
| * @returns {Object} 统计信息 | |
| */ | |
| getStats() { | |
| const total = this.accounts.length | |
| const available = this._getAvailableAccounts().length | |
| const inCooldown = total - available | |
| const usageStats = {} | |
| this.accounts.forEach(account => { | |
| const email = account.email | |
| usageStats[email] = { | |
| failures: this.failureCounts.get(email) || 0, | |
| lastUsed: this.lastUsedTimes.get(email) || null, | |
| available: this._isAccountAvailable(account) | |
| } | |
| }) | |
| return { | |
| total, | |
| available, | |
| inCooldown, | |
| currentIndex: this.currentIndex, | |
| usageStats | |
| } | |
| } | |
| /** | |
| * 获取可用账户列表 | |
| * @private | |
| */ | |
| _getAvailableAccounts() { | |
| return this.accounts.filter(account => this._isAccountAvailable(account)) | |
| } | |
| /** | |
| * 检查账户是否可用 | |
| * @param {Object} account - 账户对象 | |
| * @returns {boolean} 是否可用 | |
| * @private | |
| */ | |
| _isAccountAvailable(account) { | |
| if (!account.token) { | |
| return false | |
| } | |
| const failures = this.failureCounts.get(account.email) || 0 | |
| if (failures >= this.maxFailures) { | |
| const lastUsed = this.lastUsedTimes.get(account.email) | |
| if (lastUsed && Date.now() - lastUsed < this.cooldownPeriod) { | |
| return false // 仍在冷却期 | |
| } else { | |
| // 冷却期结束,重置失败计数 | |
| this.failureCounts.delete(account.email) | |
| } | |
| } | |
| return true | |
| } | |
| /** | |
| * 选择最少使用的账户 | |
| * @param {Array} accounts - 可用账户列表 | |
| * @returns {Object} 选中的账户 | |
| * @private | |
| */ | |
| _selectLeastUsedAccount(accounts) { | |
| if (accounts.length === 1) { | |
| return accounts[0] | |
| } | |
| // 按最后使用时间排序,选择最久未使用的 | |
| return accounts.reduce((least, current) => { | |
| const leastLastUsed = this.lastUsedTimes.get(least.email) || 0 | |
| const currentLastUsed = this.lastUsedTimes.get(current.email) || 0 | |
| return currentLastUsed < leastLastUsed ? current : least | |
| }) | |
| } | |
| /** | |
| * 轮询策略获取令牌 | |
| * @returns {string|null} 账户令牌或null | |
| * @private | |
| */ | |
| _getTokenByRoundRobin() { | |
| if (this.currentIndex >= this.accounts.length) { | |
| this.currentIndex = 0 | |
| } | |
| const account = this.accounts[this.currentIndex] | |
| this.currentIndex++ | |
| if (account && account.token) { | |
| this._recordUsage(account.email) | |
| return account.token | |
| } | |
| // 如果当前账户无效,尝试下一个 | |
| if (this.currentIndex < this.accounts.length) { | |
| return this._getTokenByRoundRobin() | |
| } | |
| return null | |
| } | |
| /** | |
| * 记录账户使用 | |
| * @param {string} email - 邮箱地址 | |
| * @private | |
| */ | |
| _recordUsage(email) { | |
| this.lastUsedTimes.set(email, Date.now()) | |
| } | |
| /** | |
| * 清理不存在账户的记录 | |
| * @private | |
| */ | |
| _cleanupRecords() { | |
| const currentEmails = new Set(this.accounts.map(acc => acc.email)) | |
| // 清理失败计数记录 | |
| for (const email of this.failureCounts.keys()) { | |
| if (!currentEmails.has(email)) { | |
| this.failureCounts.delete(email) | |
| } | |
| } | |
| // 清理使用时间记录 | |
| for (const email of this.lastUsedTimes.keys()) { | |
| if (!currentEmails.has(email)) { | |
| this.lastUsedTimes.delete(email) | |
| } | |
| } | |
| } | |
| /** | |
| * 重置所有统计数据 | |
| */ | |
| reset() { | |
| this.currentIndex = 0 | |
| this.lastUsedTimes.clear() | |
| this.failureCounts.clear() | |
| } | |
| } | |
| module.exports = AccountRotator | |