feat(provider): 添加初始化健康检查时重置使用计数功能
在初始化健康检查时重置provider的使用计数,确保初始化时状态完全重置 同时优化请求处理逻辑,提前处理OPTIONS和健康检查请求
This commit is contained in:
parent
f0201a484e
commit
484d71fcd3
2 changed files with 45 additions and 41 deletions
|
|
@ -450,7 +450,7 @@ async function initApiService(config) {
|
||||||
providerPoolManager = new ProviderPoolManager(config.providerPools, { globalConfig: config });
|
providerPoolManager = new ProviderPoolManager(config.providerPools, { globalConfig: config });
|
||||||
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
||||||
// 可以选择在这里触发一次健康检查
|
// 可以选择在这里触发一次健康检查
|
||||||
providerPoolManager.performHealthChecks();
|
providerPoolManager.performHealthChecks(true);
|
||||||
} else {
|
} else {
|
||||||
console.log('[Initialization] No provider pools configured. Using single provider mode.');
|
console.log('[Initialization] No provider pools configured. Using single provider mode.');
|
||||||
}
|
}
|
||||||
|
|
@ -555,10 +555,45 @@ function createRequestHandler(config) {
|
||||||
return async function requestHandler(req, res) {
|
return async function requestHandler(req, res) {
|
||||||
// Deep copy the config for each request to allow dynamic modification
|
// Deep copy the config for each request to allow dynamic modification
|
||||||
const currentConfig = deepmerge({}, config);
|
const currentConfig = deepmerge({}, config);
|
||||||
|
|
||||||
console.log(`\n${new Date().toLocaleString()}`);
|
console.log(`\n${new Date().toLocaleString()}`);
|
||||||
console.log(`[Server] Received request: ${req.method} http://${req.headers.host}${req.url}`);
|
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
|
// Allow overriding MODEL_PROVIDER via request header
|
||||||
const modelProviderHeader = req.headers['model-provider'];
|
const modelProviderHeader = req.headers['model-provider'];
|
||||||
if (modelProviderHeader) {
|
if (modelProviderHeader) {
|
||||||
|
|
@ -566,9 +601,7 @@ function createRequestHandler(config) {
|
||||||
console.log(`[Config] MODEL_PROVIDER overridden by header to: ${currentConfig.MODEL_PROVIDER}`);
|
console.log(`[Config] MODEL_PROVIDER overridden by header to: ${currentConfig.MODEL_PROVIDER}`);
|
||||||
//delete req.headers['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
|
// Check if the first path segment matches a MODEL_PROVIDER and switch if it does
|
||||||
const pathSegments = path.split('/').filter(segment => segment.length > 0);
|
const pathSegments = path.split('/').filter(segment => segment.length > 0);
|
||||||
if (pathSegments.length > 0) {
|
if (pathSegments.length > 0) {
|
||||||
|
|
@ -605,39 +638,6 @@ function createRequestHandler(config) {
|
||||||
return;
|
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)) {
|
if (!isAuthorized(req, requestUrl, currentConfig.REQUIRED_API_KEY)) {
|
||||||
res.writeHead(401, { 'Content-Type': 'application/json' });
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
||||||
return res.end(JSON.stringify({ error: { message: 'Unauthorized: API key is invalid or missing.' } }));
|
return res.end(JSON.stringify({ error: { message: 'Unauthorized: API key is invalid or missing.' } }));
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ export class ProviderPoolManager {
|
||||||
* @param {string} providerType - The type of the provider.
|
* @param {string} providerType - The type of the provider.
|
||||||
* @param {object} providerConfig - The configuration of the provider to mark.
|
* @param {object} providerConfig - The configuration of the provider to mark.
|
||||||
*/
|
*/
|
||||||
markProviderHealthy(providerType, providerConfig) {
|
markProviderHealthy(isInit, providerType, providerConfig) {
|
||||||
const pool = this.providerStatus[providerType];
|
const pool = this.providerStatus[providerType];
|
||||||
if (pool) {
|
if (pool) {
|
||||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||||
|
|
@ -125,6 +125,9 @@ export class ProviderPoolManager {
|
||||||
provider.config.isHealthy = true;
|
provider.config.isHealthy = true;
|
||||||
provider.config.errorCount = 0; // Reset error count on health recovery
|
provider.config.errorCount = 0; // Reset error count on health recovery
|
||||||
provider.config.lastErrorTime = null; // Reset lastErrorTime when healthy
|
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}`);
|
console.log(`[ProviderPoolManager] Marked provider as healthy: ${JSON.stringify(providerConfig)} for type ${providerType}`);
|
||||||
|
|
||||||
// 优化1: 使用防抖保存
|
// 优化1: 使用防抖保存
|
||||||
|
|
@ -137,7 +140,7 @@ export class ProviderPoolManager {
|
||||||
* Performs health checks on all providers in the pool.
|
* Performs health checks on all providers in the pool.
|
||||||
* This method would typically be called periodically (e.g., via cron job).
|
* 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...');
|
console.log('[ProviderPoolManager] Performing health checks on all providers...');
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
for (const providerType in this.providerStatus) {
|
for (const providerType in this.providerStatus) {
|
||||||
|
|
@ -158,10 +161,11 @@ export class ProviderPoolManager {
|
||||||
if (isHealthy) {
|
if (isHealthy) {
|
||||||
if (!providerStatus.config.isHealthy) {
|
if (!providerStatus.config.isHealthy) {
|
||||||
// Provider was unhealthy but is now healthy
|
// 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)`);
|
console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Marked Healthy (actual check)`);
|
||||||
} else {
|
} else {
|
||||||
// Provider was already healthy and still is
|
// Provider was already healthy and still is
|
||||||
|
this.markProviderHealthy(isInit, providerType, providerConfig);
|
||||||
console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Still Healthy`);
|
console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Still Healthy`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue