feat(provider): 添加初始化健康检查时重置使用计数功能

在初始化健康检查时重置provider的使用计数,确保初始化时状态完全重置
同时优化请求处理逻辑,提前处理OPTIONS和健康检查请求
This commit is contained in:
hex2077 2025-10-31 18:01:38 +08:00
parent f0201a484e
commit 484d71fcd3
2 changed files with 45 additions and 41 deletions

View file

@ -450,7 +450,7 @@ async function initApiService(config) {
providerPoolManager = new ProviderPoolManager(config.providerPools, { globalConfig: config });
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
// 可以选择在这里触发一次健康检查
providerPoolManager.performHealthChecks();
providerPoolManager.performHealthChecks(true);
} else {
console.log('[Initialization] No provider pools configured. Using single provider mode.');
}
@ -555,10 +555,45 @@ function createRequestHandler(config) {
return async function requestHandler(req, res) {
// Deep copy the config for each request to allow dynamic modification
const currentConfig = deepmerge({}, config);
console.log(`\n${new Date().toLocaleString()}`);
console.log(`[Server] Received request: ${req.method} http://${req.headers.host}${req.url}`);
const requestUrl = new URL(req.url, `http://${req.headers.host}`);
let path = requestUrl.pathname;
const method = req.method;
if (method === 'OPTIONS') {
// 设置 CORS 头部,允许所有来源和方法
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-goog-api-key, Model-Provider'); // 添加 Model-Provider
// OPTIONS 请求通常返回 204 No Content
res.writeHead(204);
res.end();
return;
}
// Health check endpoint - no authentication required
if (method === 'GET' && path === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({
status: 'healthy',
timestamp: new Date().toISOString(),
provider: currentConfig.MODEL_PROVIDER
}));
}
// Ignore count_tokens requests
if (path.includes('/count_tokens')) {
console.log(`[Server] Ignoring count_tokens request: ${path}`);
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({
tokens: 0,
message: 'Token counting is not supported'
}));
}
// Allow overriding MODEL_PROVIDER via request header
const modelProviderHeader = req.headers['model-provider'];
if (modelProviderHeader) {
@ -566,9 +601,7 @@ function createRequestHandler(config) {
console.log(`[Config] MODEL_PROVIDER overridden by header to: ${currentConfig.MODEL_PROVIDER}`);
//delete req.headers['model-provider']; // 保持不变,以便后端可以继续处理原始头
}
const requestUrl = new URL(req.url, `http://${req.headers.host}`);
let path = requestUrl.pathname;
// Check if the first path segment matches a MODEL_PROVIDER and switch if it does
const pathSegments = path.split('/').filter(segment => segment.length > 0);
if (pathSegments.length > 0) {
@ -605,39 +638,6 @@ function createRequestHandler(config) {
return;
}
const method = req.method;
if (method === 'OPTIONS') {
// 设置 CORS 头部,允许所有来源和方法
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-goog-api-key, Model-Provider'); // 添加 Model-Provider
// OPTIONS 请求通常返回 204 No Content
res.writeHead(204);
res.end();
return;
}
// Health check endpoint - no authentication required
if (method === 'GET' && path === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({
status: 'healthy',
timestamp: new Date().toISOString(),
provider: currentConfig.MODEL_PROVIDER
}));
}
// Ignore count_tokens requests
if (path.includes('/count_tokens')) {
console.log(`[Server] Ignoring count_tokens request: ${path}`);
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({
tokens: 0,
message: 'Token counting is not supported'
}));
}
if (!isAuthorized(req, requestUrl, currentConfig.REQUIRED_API_KEY)) {
res.writeHead(401, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ error: { message: 'Unauthorized: API key is invalid or missing.' } }));

View file

@ -117,7 +117,7 @@ export class ProviderPoolManager {
* @param {string} providerType - The type of the provider.
* @param {object} providerConfig - The configuration of the provider to mark.
*/
markProviderHealthy(providerType, providerConfig) {
markProviderHealthy(isInit, providerType, providerConfig) {
const pool = this.providerStatus[providerType];
if (pool) {
const provider = pool.find(p => p.uuid === providerConfig.uuid);
@ -125,6 +125,9 @@ export class ProviderPoolManager {
provider.config.isHealthy = true;
provider.config.errorCount = 0; // Reset error count on health recovery
provider.config.lastErrorTime = null; // Reset lastErrorTime when healthy
if (isInit) {
provider.config.usageCount = 0; // Reset usage count on health recovery
}
console.log(`[ProviderPoolManager] Marked provider as healthy: ${JSON.stringify(providerConfig)} for type ${providerType}`);
// 优化1: 使用防抖保存
@ -137,7 +140,7 @@ export class ProviderPoolManager {
* Performs health checks on all providers in the pool.
* This method would typically be called periodically (e.g., via cron job).
*/
async performHealthChecks() {
async performHealthChecks(isInit = false) {
console.log('[ProviderPoolManager] Performing health checks on all providers...');
const now = new Date();
for (const providerType in this.providerStatus) {
@ -158,10 +161,11 @@ export class ProviderPoolManager {
if (isHealthy) {
if (!providerStatus.config.isHealthy) {
// Provider was unhealthy but is now healthy
this.markProviderHealthy(providerType, providerConfig);
this.markProviderHealthy(isInit, providerType, providerConfig);
console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Marked Healthy (actual check)`);
} else {
// Provider was already healthy and still is
this.markProviderHealthy(isInit, providerType, providerConfig);
console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Still Healthy`);
}
} else {