Browse Source

测速日志优化

Taio_O 1 week ago
parent
commit
fc652adb70
4 changed files with 67 additions and 69 deletions
  1. 1 0
      env.example
  2. 18 43
      src/core/multiSubscriptionManager.js
  3. 3 7
      src/core/scheduler.js
  4. 45 19
      src/core/speedTester.js

+ 1 - 0
env.example

@@ -22,6 +22,7 @@ CLASH_SUBSCRIPTION_URL=http://so.xfxssr.me/api/v1/client/subscribe?token=7854d59
 SPEED_TEST_INTERVAL=10 # 分钟
 SPEED_TEST_TIMEOUT=10000 # 毫秒
 SPEED_TEST_URLS=https://www.google.com,https://www.youtube.com,https://www.github.com
+NODE_TEST_DELAY=500 # 节点间测试延迟(毫秒,默认500ms)
 
 # 通知配置
 NOTIFICATION_FAILURE_THRESHOLD=3 # 连续失败次数阈值

+ 18 - 43
src/core/multiSubscriptionManager.js

@@ -10,6 +10,7 @@ class MultiSubscriptionManager {
     this.updateInterval = parseInt(process.env.SUBSCRIPTION_UPDATE_INTERVAL) || 3600000; // 默认1小时
     this.updateTimer = null;
     this.isUpdating = false; // 添加更新锁,防止并发更新
+    this.speedTestDebounceTimer = null; // 防抖定时器
   }
 
   /**
@@ -55,27 +56,22 @@ class MultiSubscriptionManager {
       try {
         config = yaml.parse(rawContent);
         if (config && typeof config === 'object' && Array.isArray(config.proxies)) {
-          logger.info('成功解析为YAML格式', { subscriptionId: subscription.id });
           yamlParsed = true;
         } else {
-          logger.info('YAML解析结果不是有效的订阅对象,进入Base64解码流程', { subscriptionId: subscription.id });
           throw new Error('不是有效的YAML订阅');
         }
       } catch (error) {
-        if (!yamlParsed) logger.info('不是YAML格式,尝试Base64解码', { subscriptionId: subscription.id });
+        // 尝试Base64解码
         
         // 2. 不是YAML,尝试整体Base64解码
         let decoded;
         try {
           decoded = Buffer.from(rawContent, 'base64').toString('utf8');
-          logger.info('Base64解码成功,内容长度:', decoded.length, { subscriptionId: subscription.id });
           
           // 如果解码后包含ss://、vmess://、trojan://,说明是明文链接合集
           if (/ss:\/\/|vmess:\/\/|trojan:\/\//.test(decoded)) {
-            logger.info('检测到代理链接,转换为Clash格式', { subscriptionId: subscription.id });
             config = this.convertShadowsocksToClash(decoded);
           } else {
-            logger.info('整体解码后不是代理链接,尝试多行Base64解码', { subscriptionId: subscription.id });
             // 3. 如果整体解码后不是明文链接合集,尝试多行Base64(每行一个链接)
             const lines = rawContent.split('\n').filter(line => line.trim());
             logger.info('原始内容行数:', lines.length, { subscriptionId: subscription.id });
@@ -117,16 +113,7 @@ class MultiSubscriptionManager {
         throw new Error('订阅配置中没有找到有效的代理节点');
       }
       
-      // 只在节点数量较多时显示前几个名称
-      const proxyNames = config.proxies.length > 10 
-        ? config.proxies.slice(0, 3).map(p => p.name)
-        : config.proxies.map(p => p.name);
-      
-      logger.info('订阅配置解析成功', {
-        proxyCount: config.proxies.length,
-        proxyNames: proxyNames,
-        subscriptionId: subscription.id
-      });
+      // 解析成功
 
       return config;
     } catch (error) {
@@ -178,10 +165,7 @@ class MultiSubscriptionManager {
         }
       }
       
-      logger.info(`解析完成,去重后节点数量: ${newNodes.length}`, {
-        subscriptionId: subscription.id,
-        originalCount: config.proxies.length
-      });
+      // 解析完成,去重后节点数量: ${newNodes.length}
 
       if (newNodes.length === 0) {
         logger.warn('订阅中没有找到有效的节点', { subscriptionId: subscription.id });
@@ -280,17 +264,17 @@ class MultiSubscriptionManager {
       // 提交事务
       await transaction.commit();
 
-      logger.info(`订阅节点更新完成 - 新增${added}个,更新${updated}个,移除${removed}个,实际节点数${actualNodeCount}`, {
-        subscriptionId: subscription.id,
-        subscriptionName: subscription.name
-      });
+      logger.info(`订阅更新: ${subscription.name} - 新增${added}个,更新${updated}个,移除${removed}个`);
 
       // 如果有新增或更新的节点,触发测速
       if (added > 0 || updated > 0) {
-        logger.info('检测到节点更新,准备触发测速...');
         // 延迟5秒后触发测速,确保数据库操作完成
-        setTimeout(() => {
+        if (this.speedTestDebounceTimer) {
+          clearTimeout(this.speedTestDebounceTimer);
+        }
+        this.speedTestDebounceTimer = setTimeout(() => {
           this.triggerSpeedTest();
+          this.speedTestDebounceTimer = null;
         }, 5000);
       }
 
@@ -320,17 +304,13 @@ class MultiSubscriptionManager {
     
     try {
       const subscriptions = await this.getActiveSubscriptions();
-      logger.info(`开始更新所有订阅,共${subscriptions.length}个活跃订阅`);
+      logger.info(`更新 ${subscriptions.length} 个订阅`);
 
       const results = [];
       
       // 串行执行订阅更新,避免并发问题
       for (const subscription of subscriptions) {
         try {
-          logger.info(`开始更新订阅: ${subscription.name}`, {
-            subscriptionId: subscription.id
-          });
-          
           const result = await this.updateSubscriptionNodes(subscription);
           results.push({
             subscriptionId: subscription.id,
@@ -338,14 +318,6 @@ class MultiSubscriptionManager {
             ...result
           });
           
-          logger.info(`订阅更新完成: ${subscription.name}`, {
-            subscriptionId: subscription.id,
-            added: result.added,
-            updated: result.updated,
-            removed: result.removed,
-            actualNodeCount: result.actualNodeCount
-          });
-          
           // 每个订阅更新后稍作延迟,避免过于频繁的数据库操作
           await new Promise(resolve => setTimeout(resolve, 2000));
           
@@ -369,10 +341,13 @@ class MultiSubscriptionManager {
       );
 
       if (hasUpdates) {
-        logger.info('检测到节点更新,准备触发测速...');
         // 延迟5秒后触发测速,确保数据库操作完成
-        setTimeout(() => {
+        if (this.speedTestDebounceTimer) {
+          clearTimeout(this.speedTestDebounceTimer);
+        }
+        this.speedTestDebounceTimer = setTimeout(() => {
           this.triggerSpeedTest();
+          this.speedTestDebounceTimer = null;
         }, 5000);
       }
 
@@ -471,7 +446,7 @@ class MultiSubscriptionManager {
     const proxies = [];
     const proxyKeys = new Set(); // 用于去重
     
-    logger.info(`开始解析Shadowsocks内容,共${lines.length}行`);
+          // 开始解析Shadowsocks内容
     
     let ssCount = 0;
     let vmessCount = 0;
@@ -509,7 +484,7 @@ class MultiSubscriptionManager {
       }
     }
     
-    logger.info(`解析完成 - SS: ${ssCount}个, VMess: ${vmessCount}个, Trojan: ${trojanCount}个, 成功解析: ${proxies.length}个, 重复跳过: ${duplicateCount}个, 失败: ${errorCount}个`);
+          // 解析完成 - SS: ${ssCount}个, VMess: ${vmessCount}个, Trojan: ${trojanCount}个
     
     if (proxies.length === 0) {
       logger.warn('没有找到任何有效的代理节点');

+ 3 - 7
src/core/scheduler.js

@@ -101,8 +101,6 @@ class Scheduler {
         return;
       }
 
-      logger.info(`找到 ${nodes.length} 个节点,开始测试`);
-
       // 批量测试节点
       const testResults = await this.speedTester.testNodes(nodes);
 
@@ -120,7 +118,7 @@ class Scheduler {
 
       // 如果有高延迟节点,进行重测
       if (highLatencyNodes.length > 0) {
-        logger.info(`发现 ${highLatencyNodes.length} 个高延迟节点,开始重测`);
+        logger.info(`重测 ${highLatencyNodes.length} 个高延迟节点`);
         
         // 重测高延迟节点
         const retestResults = await this.speedTester.testNodes(highLatencyNodes);
@@ -135,9 +133,6 @@ class Scheduler {
             // 如果重测结果更好,使用重测结果
             if (retestResult.isSuccess && retestResult.latency && retestResult.latency <= 2000) {
               testResults[originalIndex] = retestResult;
-              logger.info(`节点 ${node.name} 重测成功,延迟从 ${highLatencyResults[i].latency}ms 改善到 ${retestResult.latency}ms`);
-            } else {
-              logger.info(`节点 ${node.name} 重测后仍为高延迟: ${retestResult.latency || '超时'}ms`);
             }
           }
         }
@@ -150,7 +145,8 @@ class Scheduler {
       await this.sendTestSummary(nodes, testResults);
 
       const duration = Date.now() - startTime;
-      logger.info(`定时测速完成 - ${nodes.length}个节点,${testResults.filter(r => r.isSuccess).length}个成功,耗时${duration}ms`);
+      const successCount = testResults.filter(r => r.isSuccess).length;
+      logger.info(`测速完成: ${successCount}/${nodes.length}个成功, 耗时${duration}ms`);
 
     } catch (error) {
       logger.error('定时速度测试失败', { error: error.message });

+ 45 - 19
src/core/speedTester.js

@@ -1066,37 +1066,63 @@ class SpeedTester {
   }
 
   /**
-   * 批量测试节点
+   * 批量测试节点 - 顺序测试,一个接一个
    */
   async testNodes(nodes) {
-    logger.info(`开始批量测试 ${nodes.length} 个节点`);
+    logger.info(`开始测试 ${nodes.length} 个节点`);
     
     const results = [];
-    const concurrency = parseInt(process.env.CONCURRENCY) || 5;
+    const nodeDelay = parseInt(process.env.NODE_TEST_DELAY) || 500;
+    const successResults = [];
+    const failedResults = [];
     
-    // 分批处理,控制并发数
-    for (let i = 0; i < nodes.length; i += concurrency) {
-      const batch = nodes.slice(i, i + concurrency);
-      const batchPromises = batch.map(node => this.testNode(node));
-      
-      const batchResults = await Promise.allSettled(batchPromises);
+    // 顺序测试每个节点
+    for (let i = 0; i < nodes.length; i++) {
+      const node = nodes[i];
       
-      batchResults.forEach((result, index) => {
-        if (result.status === 'fulfilled') {
-          results.push(result.value);
+      try {
+        const result = await this.testNode(node);
+        results.push(result);
+        
+        // 分类记录结果
+        if (result.isSuccess) {
+          successResults.push({ name: node.name, latency: result.latency });
         } else {
-          logger.error(`节点测试失败: ${batch[index].name} - ${result.reason}`);
+          failedResults.push({ name: node.name, error: result.error });
         }
-      });
+        
+      } catch (error) {
+        logger.error(`节点测试异常: ${node.name} - ${error.message}`);
+        results.push({
+          nodeId: node.id,
+          nodeName: node.name,
+          isSuccess: false,
+          error: error.message,
+          latency: -1,
+          timestamp: new Date()
+        });
+        failedResults.push({ name: node.name, error: error.message });
+      }
       
-      // 批次间延迟,避免过于频繁的请求
-      if (i + concurrency < nodes.length) {
-        await new Promise(resolve => setTimeout(resolve, 1000));
+      // 节点间延迟
+      if (i < nodes.length - 1) {
+        await new Promise(resolve => setTimeout(resolve, nodeDelay));
       }
     }
     
-    const successCount = results.filter(r => r.isSuccess).length;
-    logger.info(`批量测试完成: ${successCount}/${nodes.length} 个节点连通`);
+    // 输出简洁的测试结果
+    logger.info(`测试完成: ${successResults.length}个成功, ${failedResults.length}个失败`);
+    
+    // 只显示前5个成功和失败的节点
+    if (successResults.length > 0) {
+      const topSuccess = successResults.slice(0, 5);
+      logger.info(`成功节点: ${topSuccess.map(r => `${r.name}(${r.latency}ms)`).join(', ')}${successResults.length > 5 ? '...' : ''}`);
+    }
+    
+    if (failedResults.length > 0) {
+      const topFailed = failedResults.slice(0, 5);
+      logger.warn(`失败节点: ${topFailed.map(r => r.name).join(', ')}${failedResults.length > 5 ? '...' : ''}`);
+    }
     
     return results;
   }