fix(provider): 优化节点刷新逻辑
- 修改节点刷新逻辑,当健康节点少于5个时立即刷新 - 移除刷新时的随机延迟以避免并发问题 - 更新Claude Kiro版本号至0.8.140 - 在初始化API服务时添加isReady参数控制预热逻辑 - 在modal.js中排除更多字段
This commit is contained in:
parent
f8faee93fc
commit
fe131b33d2
5 changed files with 30 additions and 45 deletions
|
|
@ -31,7 +31,7 @@ const KIRO_CONSTANTS = {
|
||||||
AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests
|
AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests
|
||||||
TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking)
|
TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking)
|
||||||
USER_AGENT: 'KiroIDE',
|
USER_AGENT: 'KiroIDE',
|
||||||
KIRO_VERSION: '0.7.5',
|
KIRO_VERSION: '0.8.140',
|
||||||
CONTENT_TYPE_JSON: 'application/json',
|
CONTENT_TYPE_JSON: 'application/json',
|
||||||
ACCEPT_JSON: 'application/json',
|
ACCEPT_JSON: 'application/json',
|
||||||
AUTH_METHOD_SOCIAL: 'social',
|
AUTH_METHOD_SOCIAL: 'social',
|
||||||
|
|
@ -403,7 +403,7 @@ export class KiroApiService {
|
||||||
// 配置 HTTP/HTTPS agent 限制连接池大小,避免资源泄漏
|
// 配置 HTTP/HTTPS agent 限制连接池大小,避免资源泄漏
|
||||||
const httpAgent = new http.Agent({
|
const httpAgent = new http.Agent({
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
maxSockets: 100, // 每个主机最多 10 个连接
|
maxSockets: 100, // 每个主机最多 100 个连接
|
||||||
maxFreeSockets: 5, // 最多保留 5 个空闲连接
|
maxFreeSockets: 5, // 最多保留 5 个空闲连接
|
||||||
timeout: KIRO_CONSTANTS.AXIOS_TIMEOUT,
|
timeout: KIRO_CONSTANTS.AXIOS_TIMEOUT,
|
||||||
});
|
});
|
||||||
|
|
@ -2735,7 +2735,7 @@ async saveCredentialsToFile(filePath, newData) {
|
||||||
}
|
}
|
||||||
const fullUrl = `${usageLimitsUrl}?${params.toString()}`;
|
const fullUrl = `${usageLimitsUrl}?${params.toString()}`;
|
||||||
|
|
||||||
// 构建请求头
|
// 动态生成 headers
|
||||||
const machineId = generateMachineIdFromConfig({
|
const machineId = generateMachineIdFromConfig({
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
profileArn: this.profileArn,
|
profileArn: this.profileArn,
|
||||||
|
|
|
||||||
|
|
@ -108,32 +108,7 @@ export class ProviderPoolManager {
|
||||||
|
|
||||||
if (configPath && fs.existsSync(configPath)) {
|
if (configPath && fs.existsSync(configPath)) {
|
||||||
try {
|
try {
|
||||||
const fileContent = fs.readFileSync(configPath, 'utf8');
|
if (true) {
|
||||||
const data = JSON.parse(fileContent);
|
|
||||||
|
|
||||||
// 获取对应的适配器
|
|
||||||
const tempConfig = {
|
|
||||||
...config,
|
|
||||||
MODEL_PROVIDER: providerType
|
|
||||||
};
|
|
||||||
const serviceAdapter = getServiceAdapter(tempConfig);
|
|
||||||
|
|
||||||
// 调用提供商适配器内的 isExpiryDateNear 方法
|
|
||||||
let needsRefresh = false;
|
|
||||||
if (typeof serviceAdapter.isExpiryDateNear === 'function') {
|
|
||||||
// 适配器内部自行判断,不传参
|
|
||||||
needsRefresh = serviceAdapter.isExpiryDateNear();
|
|
||||||
this._log('info', `Node ${providerStatus.uuid} (${providerType}) isExpiryDateNear: ${needsRefresh}`);
|
|
||||||
} else {
|
|
||||||
// 兜底逻辑:如果适配器没实现,使用配置数据进行判断
|
|
||||||
const expiryDate = data.expiry_date || data.expires_at || data.expiry;
|
|
||||||
if (expiryDate) {
|
|
||||||
const expiry = new Date(expiryDate).getTime();
|
|
||||||
needsRefresh = (expiry - Date.now()) < 24 * 60 * 60 * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsRefresh) {
|
|
||||||
this._log('warn', `Node ${providerStatus.uuid} (${providerType}) is near expiration. Enqueuing refresh...`);
|
this._log('warn', `Node ${providerStatus.uuid} (${providerType}) is near expiration. Enqueuing refresh...`);
|
||||||
this._enqueueRefresh(providerType, providerStatus);
|
this._enqueueRefresh(providerType, providerStatus);
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +116,7 @@ export class ProviderPoolManager {
|
||||||
this._log('error', `Failed to check expiry for node ${providerStatus.uuid}: ${err.message}`);
|
this._log('error', `Failed to check expiry for node ${providerStatus.uuid}: ${err.message}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._log('debug', `Node ${providerStatus.uuid} (${providerType}) has no valid config file path or file does not exist.`);
|
this._log('debug', `Node ${providerStatus.uuid} (${providerType}) has no valid config file path or file does not exist.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,6 +178,14 @@ export class ProviderPoolManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断提供商池内的总可用节点数,小于5个时,不等待缓冲,直接加入刷新队列
|
||||||
|
const healthyCount = this.getHealthyCount(providerType);
|
||||||
|
if (healthyCount < 5) {
|
||||||
|
this._log('info', `Provider ${providerType} has only ${healthyCount} healthy nodes. Bypassing buffer and enqueuing refresh for ${uuid} immediately.`);
|
||||||
|
this._enqueueRefreshImmediate(providerType, providerStatus, force);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化缓冲队列
|
// 初始化缓冲队列
|
||||||
if (!this.refreshBufferQueues[providerType]) {
|
if (!this.refreshBufferQueues[providerType]) {
|
||||||
this.refreshBufferQueues[providerType] = new Map(); // 使用 Map 自动去重
|
this.refreshBufferQueues[providerType] = new Map(); // 使用 Map 自动去重
|
||||||
|
|
@ -383,9 +366,9 @@ export class ProviderPoolManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加5秒内的随机等待时间,避免并发刷新时的冲突
|
// 添加5秒内的随机等待时间,避免并发刷新时的冲突
|
||||||
const randomDelay = Math.floor(Math.random() * 5000);
|
// const randomDelay = Math.floor(Math.random() * 5000);
|
||||||
this._log('info', `Starting token refresh for node ${providerStatus.uuid} (${providerType}) with ${randomDelay}ms delay`);
|
// this._log('info', `Starting token refresh for node ${providerStatus.uuid} (${providerType}) with ${randomDelay}ms delay`);
|
||||||
await new Promise(resolve => setTimeout(resolve, randomDelay));
|
// await new Promise(resolve => setTimeout(resolve, randomDelay));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 增加刷新计数
|
// 增加刷新计数
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ async function startServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize API services
|
// Initialize API services
|
||||||
const services = await initApiService(CONFIG);
|
const services = await initApiService(CONFIG, true);
|
||||||
|
|
||||||
// Initialize UI management features
|
// Initialize UI management features
|
||||||
initializeUIManagement(CONFIG);
|
initializeUIManagement(CONFIG);
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ async function scanProviderDirectory(dirPath, linkedPaths, newProviders, options
|
||||||
* @param {Object} config - The server configuration
|
* @param {Object} config - The server configuration
|
||||||
* @returns {Promise<Object>} The initialized services
|
* @returns {Promise<Object>} The initialized services
|
||||||
*/
|
*/
|
||||||
export async function initApiService(config) {
|
export async function initApiService(config, isReady = false) {
|
||||||
|
|
||||||
if (config.providerPools && Object.keys(config.providerPools).length > 0) {
|
if (config.providerPools && Object.keys(config.providerPools).length > 0) {
|
||||||
providerPoolManager = new ProviderPoolManager(config.providerPools, {
|
providerPoolManager = new ProviderPoolManager(config.providerPools, {
|
||||||
|
|
@ -175,16 +175,18 @@ export async function initApiService(config) {
|
||||||
});
|
});
|
||||||
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
||||||
|
|
||||||
// --- V2: 触发系统预热 ---
|
if(isReady){
|
||||||
// 预热逻辑是异步的,不会阻塞服务器启动
|
// --- V2: 触发系统预热 ---
|
||||||
providerPoolManager.warmupNodes().catch(err => {
|
// 预热逻辑是异步的,不会阻塞服务器启动
|
||||||
console.error(`[Initialization] Warmup failed: ${err.message}`);
|
providerPoolManager.warmupNodes().catch(err => {
|
||||||
});
|
console.error(`[Initialization] Warmup failed: ${err.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
// 检查并刷新即将过期的节点(异步调用,不阻塞启动)
|
// 检查并刷新即将过期的节点(异步调用,不阻塞启动)
|
||||||
providerPoolManager.checkAndRefreshExpiringNodes().catch(err => {
|
providerPoolManager.checkAndRefreshExpiringNodes().catch(err => {
|
||||||
console.error(`[Initialization] Check and refresh expiring nodes failed: ${err.message}`);
|
console.error(`[Initialization] Check and refresh expiring nodes failed: ${err.message}`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 健康检查将在服务器完全启动后执行
|
// 健康检查将在服务器完全启动后执行
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -674,7 +674,7 @@ function getFieldOrder(provider) {
|
||||||
const excludedFields = [
|
const excludedFields = [
|
||||||
'isHealthy', 'lastUsed', 'usageCount', 'errorCount', 'lastErrorTime',
|
'isHealthy', 'lastUsed', 'usageCount', 'errorCount', 'lastErrorTime',
|
||||||
'uuid', 'isDisabled', 'lastHealthCheckTime', 'lastHealthCheckModel', 'lastErrorMessage',
|
'uuid', 'isDisabled', 'lastHealthCheckTime', 'lastHealthCheckModel', 'lastErrorMessage',
|
||||||
'notSupportedModels'
|
'notSupportedModels', 'refreshCount', 'needsRefresh'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 从 getProviderTypeFields 获取字段顺序映射
|
// 从 getProviderTypeFields 获取字段顺序映射
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue