From b32efa91311cf0de4feaa2a3db62068a703d6baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 5 Apr 2025 11:45:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/helper/rkey.ts | 80 +++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/src/core/helper/rkey.ts b/src/core/helper/rkey.ts index ecc3634d..5425279c 100644 --- a/src/core/helper/rkey.ts +++ b/src/core/helper/rkey.ts @@ -7,6 +7,11 @@ interface ServerRkeyData { expired_time: number; } +interface UrlFailureInfo { + count: number; + lastTimestamp: number; +} + export class RkeyManager { serverUrl: string[] = []; logger: LogWrapper; @@ -15,8 +20,7 @@ export class RkeyManager { private_rkey: '', expired_time: 0, }; - private failureCount: number = 0; - private lastFailureTimestamp: number = 0; + private urlFailures: Map = new Map(); private readonly FAILURE_LIMIT: number = 8; private readonly ONE_DAY: number = 24 * 60 * 60 * 1000; @@ -26,34 +30,74 @@ export class RkeyManager { } async getRkey() { - const now = new Date().getTime(); - if (now - this.lastFailureTimestamp > this.ONE_DAY) { - this.failureCount = 0; // 重置失败计数器 - } - - if (this.failureCount >= this.FAILURE_LIMIT) { - this.logger.logError('[Rkey] 服务存在异常, 图片使用FallBack机制'); - throw new Error('获取rkey失败次数过多,请稍后再试'); + const availableUrls = this.getAvailableUrls(); + if (availableUrls.length === 0) { + this.logger.logError('[Rkey] 所有服务均已禁用, 图片使用FallBack机制'); + throw new Error('获取rkey失败:所有服务URL均已被禁用'); } if (this.isExpired()) { try { await this.refreshRkey(); } catch (e) { - throw new Error(`${e}`);//外抛 + throw new Error(`${e}`); } } return this.rkeyData; } + private getAvailableUrls(): string[] { + return this.serverUrl.filter(url => !this.isUrlDisabled(url)); + } + + private isUrlDisabled(url: string): boolean { + const failureInfo = this.urlFailures.get(url); + if (!failureInfo) return false; + + const now = new Date().getTime(); + // 如果已经过了一天,重置失败计数 + if (now - failureInfo.lastTimestamp > this.ONE_DAY) { + failureInfo.count = 0; + this.urlFailures.set(url, failureInfo); + return false; + } + + return failureInfo.count >= this.FAILURE_LIMIT; + } + + private updateUrlFailure(url: string) { + const now = new Date().getTime(); + const failureInfo = this.urlFailures.get(url) || { count: 0, lastTimestamp: 0 }; + + // 如果已经过了一天,重置失败计数 + if (now - failureInfo.lastTimestamp > this.ONE_DAY) { + failureInfo.count = 1; + } else { + failureInfo.count++; + } + + failureInfo.lastTimestamp = now; + this.urlFailures.set(url, failureInfo); + + if (failureInfo.count >= this.FAILURE_LIMIT) { + this.logger.logError(`[Rkey] URL ${url} 已被禁用,失败次数达到 ${this.FAILURE_LIMIT} 次`); + } + } + isExpired(): boolean { const now = new Date().getTime() / 1000; return now > this.rkeyData.expired_time; } async refreshRkey() { - //刷新rkey - for (const url of this.serverUrl) { + const availableUrls = this.getAvailableUrls(); + + if (availableUrls.length === 0) { + this.logger.logError('[Rkey] 所有服务均已禁用'); + throw new Error('获取rkey失败:所有服务URL均已被禁用'); + } + + for (const url of availableUrls) { try { const temp = await RequestUtil.HttpGetJson(url, 'GET'); this.rkeyData = { @@ -61,15 +105,13 @@ export class RkeyManager { private_rkey: temp.private_rkey.slice(6), expired_time: temp.expired_time }; - this.failureCount = 0; return; } catch (e) { this.logger.logError(`[Rkey] 异常服务 ${url} 异常 / `, e); - this.failureCount++; - this.lastFailureTimestamp = new Date().getTime(); - //是否为最后一个url - if (url === this.serverUrl[this.serverUrl.length - 1]) { - throw new Error(`获取rkey失败: ${e}`);//外抛 + this.updateUrlFailure(url); + + if (url === availableUrls[availableUrls.length - 1]) { + throw new Error(`获取rkey失败: ${e}`); } } }