fix(provider): 修复令牌刷新机制中的并发和状态问题

修复 provider-pool-manager 中令牌刷新逻辑的多个问题:
- 将最大刷新次数从 3 更正为 5 以匹配注释
- 添加防并发机制,避免同一节点重复刷新
- 刷新成功后统一重置 needsRefresh、refreshCount 并记录 lastRefreshTime
- 在 Grok 服务中添加重置刷新状态的调用
- 防止 30 秒内的重复刷新请求

这些更改解决了因 401 错误滞后导致的重复刷新、状态不一致以及节点被过早标记为不健康的问题。
This commit is contained in:
hex2077 2026-03-26 12:32:09 +08:00
parent c5d475c7e0
commit 9bbde40e4c
3 changed files with 33 additions and 7 deletions

View file

@ -1 +1 @@
2.11.8
2.11.9

View file

@ -185,10 +185,15 @@ export class GrokApiService {
}
async refreshToken() {
try {
// await this.getUsageLimits(); return Promise.resolve();
} catch (error) {
return Promise.reject(error);
try {
// await this.getUsageLimits(); return Promise.resolve();
const poolManager = getProviderPoolManager();
if (poolManager && this.uuid) {
poolManager.resetProviderRefreshStatus(MODEL_PROVIDER.GROK_CUSTOM, this.uuid);
}
} catch (error) {
logger.error('[Grok] Failed to initialize authentication:', error);
throw new Error(`Failed to refreshToken.`);
}
}

View file

@ -363,9 +363,9 @@ export class ProviderPoolManager {
// 检查刷新次数是否已达上限最大5次
const currentRefreshCount = config.refreshCount || 0;
if (currentRefreshCount >= 5 && !force) {
this._log('warn', `Node ${providerStatus.uuid} has reached maximum refresh count (3), marking as unhealthy`);
this._log('warn', `Node ${providerStatus.uuid} has reached maximum refresh count (5), marking as unhealthy`);
// 标记为不健康
this.markProviderUnhealthyImmediately(providerType, config, 'Maximum refresh count (3) reached');
this.markProviderUnhealthyImmediately(providerType, config, 'Maximum refresh count (5) reached');
return;
}
@ -392,6 +392,11 @@ export class ProviderPoolManager {
force ? await serviceAdapter.forceRefreshToken() : await serviceAdapter.refreshToken()
const duration = Date.now() - startTime;
this._log('info', `Token refresh successful for node ${providerStatus.uuid} (Duration: ${duration}ms)`);
// 刷新成功,统一重置状态
config.needsRefresh = false;
config.refreshCount = 0;
config.lastRefreshTime = Date.now(); // 记录最后刷新成功时间
} else {
throw new Error(`refreshToken method not implemented for ${providerType}`);
}
@ -1291,6 +1296,20 @@ export class ProviderPoolManager {
const provider = this._findProvider(providerType, providerConfig.uuid);
if (provider) {
// 防并发机制 A: 如果已经在刷新中,忽略请求
if (this.refreshingUuids.has(provider.uuid)) {
this._log('debug', `Provider ${providerConfig.uuid} is already in refresh queue, ignoring duplicate request.`);
return;
}
// 防并发机制 B: 如果 30 秒内刚刷新过,忽略请求(防止滞后的 401 错误导致重复刷新)
const now = Date.now();
const lastRefreshTime = provider.config.lastRefreshTime || 0;
if (now - lastRefreshTime < 30000) {
this._log('info', `Provider ${providerConfig.uuid} was refreshed recently (${Math.round((now - lastRefreshTime)/1000)}s ago), ignoring refresh request.`);
return;
}
provider.config.needsRefresh = true;
this._log('info', `Marked provider ${providerConfig.uuid} as needsRefresh. Enqueuing...`);
@ -1453,6 +1472,7 @@ export class ProviderPoolManager {
provider.config.errorCount = 0;
provider.config.refreshCount = 0;
provider.config.needsRefresh = false;
provider.config.lastRefreshTime = Date.now(); // 标记为健康时也视为刚刷新完成
provider.config.lastErrorTime = null;
provider.config.lastErrorMessage = null;
provider.config._lastSelectionSeq = 0;
@ -1498,6 +1518,7 @@ export class ProviderPoolManager {
if (provider) {
provider.config.needsRefresh = false;
provider.config.refreshCount = 0;
provider.config.lastRefreshTime = Date.now(); // 显式重置时也更新刷新时间
// 更新为可用
provider.config.lastHealthCheckTime = new Date().toISOString();
// 标记为健康,以便立即投入使用