File size: 8,102 Bytes
0fadeb3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/**
 * 内存优化效果测试脚本
 * 用于验证服务的内存使用是否控制在目标范围内(约20MB)
 */

const http = require('http');
const { spawn } = require('child_process');
const path = require('path');

// 配置
const PORT = process.env.PORT || 9876;
const BASE_URL = `http://localhost:${PORT}`;
const TEST_DURATION_MS = 60000; // 测试持续时间:60秒
const SAMPLE_INTERVAL_MS = 2000; // 采样间隔:2秒
const REQUEST_INTERVAL_MS = 1000; // 请求间隔:1秒

// 内存采样数据
const memorySamples = [];
let serverProcess = null;
let testStartTime = null;

/**
 * 格式化内存大小
 */
function formatMemory(bytes) {
  const mb = bytes / 1024 / 1024;
  return `${mb.toFixed(2)} MB`;
}

/**
 * 发送HTTP请求
 */
function sendRequest(urlPath, method = 'GET', body = null) {
  return new Promise((resolve, reject) => {
    const url = new URL(urlPath, BASE_URL);
    const options = {
      hostname: url.hostname,
      port: url.port,
      path: url.pathname,
      method: method,
      headers: {
        'Content-Type': 'application/json',
      },
      timeout: 5000
    };

    const req = http.request(options, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => resolve({ status: res.statusCode, data }));
    });

    req.on('error', reject);
    req.on('timeout', () => {
      req.destroy();
      reject(new Error('Request timeout'));
    });

    if (body) {
      req.write(JSON.stringify(body));
    }
    req.end();
  });
}

/**
 * 获取服务器内存使用情况(通过 /v1/memory 端点)
 */
async function getServerMemory() {
  try {
    const response = await sendRequest('/v1/memory');
    if (response.status === 200) {
      const data = JSON.parse(response.data);
      return data;
    }
  } catch (e) {
    // 如果端点不存在,返回 null
  }
  return null;
}

/**
 * 模拟API请求
 */
async function simulateLoad() {
  const requests = [
    { path: '/v1/models', method: 'GET' },
    { path: '/health', method: 'GET' },
    { path: '/v1/chat/completions', method: 'POST', body: {
      model: 'test-model',
      messages: [{ role: 'user', content: 'Hello, this is a test message for memory optimization.' }],
      stream: false
    }},
  ];

  const randomRequest = requests[Math.floor(Math.random() * requests.length)];
  try {
    await sendRequest(randomRequest.path, randomRequest.method, randomRequest.body);
  } catch (e) {
    // 忽略请求错误,重点是测试内存
  }
}

/**
 * 启动服务器进程
 */
function startServer() {
  return new Promise((resolve, reject) => {
    console.log('🚀 启动服务器...');
    
    const serverPath = path.join(__dirname, '..', 'src', 'server', 'index.js');
    serverProcess = spawn('node', ['--expose-gc', serverPath], {
      cwd: path.join(__dirname, '..'),
      env: { ...process.env, PORT: PORT.toString() },
      stdio: ['pipe', 'pipe', 'pipe']
    });

    let started = false;
    
    serverProcess.stdout.on('data', (data) => {
      const output = data.toString();
      if (!started && (output.includes('listening') || output.includes('Server started') || output.includes('服务器'))) {
        started = true;
        setTimeout(resolve, 1000); // 等待服务器完全就绪
      }
    });

    serverProcess.stderr.on('data', (data) => {
      console.error('Server stderr:', data.toString());
    });

    serverProcess.on('error', reject);

    // 超时处理
    setTimeout(() => {
      if (!started) {
        started = true;
        resolve(); // 即使没有检测到启动消息,也继续测试
      }
    }, 5000);
  });
}

/**
 * 停止服务器进程
 */
function stopServer() {
  if (serverProcess) {
    console.log('\n🛑 停止服务器...');
    serverProcess.kill('SIGTERM');
    serverProcess = null;
  }
}

/**
 * 采集内存样本
 */
async function collectMemorySample() {
  const memoryInfo = await getServerMemory();
  const elapsed = Date.now() - testStartTime;
  
  if (memoryInfo) {
    memorySamples.push({
      time: elapsed,
      heapUsed: memoryInfo.heapUsed,
      heapTotal: memoryInfo.heapTotal,
      rss: memoryInfo.rss,
      external: memoryInfo.external
    });
    
    console.log(`📊 [${(elapsed/1000).toFixed(1)}s] Heap: ${formatMemory(memoryInfo.heapUsed)} / ${formatMemory(memoryInfo.heapTotal)}, RSS: ${formatMemory(memoryInfo.rss)}`);
  } else {
    // 如果没有内存端点,使用进程内存估算
    const usage = process.memoryUsage();
    console.log(`📊 [${(elapsed/1000).toFixed(1)}s] 测试进程内存 - Heap: ${formatMemory(usage.heapUsed)}, RSS: ${formatMemory(usage.rss)}`);
  }
}

/**
 * 分析内存数据
 */
function analyzeResults() {
  if (memorySamples.length === 0) {
    console.log('\n⚠️ 没有采集到内存数据(服务器可能没有 /v1/memory 端点)');
    console.log('请手动检查服务器日志中的内存使用情况。');
    return;
  }

  const heapValues = memorySamples.map(s => s.heapUsed);
  const rssValues = memorySamples.map(s => s.rss);

  const heapMin = Math.min(...heapValues);
  const heapMax = Math.max(...heapValues);
  const heapAvg = heapValues.reduce((a, b) => a + b, 0) / heapValues.length;

  const rssMin = Math.min(...rssValues);
  const rssMax = Math.max(...rssValues);
  const rssAvg = rssValues.reduce((a, b) => a + b, 0) / rssValues.length;

  console.log('\n📈 内存统计分析');
  console.log('═'.repeat(50));
  console.log(`采样数量: ${memorySamples.length}`);
  console.log(`测试时长: ${((memorySamples[memorySamples.length-1]?.time || 0) / 1000).toFixed(1)} 秒`);
  console.log('');
  console.log('Heap 使用:');
  console.log(`  最小: ${formatMemory(heapMin)}`);
  console.log(`  最大: ${formatMemory(heapMax)}`);
  console.log(`  平均: ${formatMemory(heapAvg)}`);
  console.log('');
  console.log('RSS (常驻内存):');
  console.log(`  最小: ${formatMemory(rssMin)}`);
  console.log(`  最大: ${formatMemory(rssMax)}`);
  console.log(`  平均: ${formatMemory(rssAvg)}`);
  console.log('');

  // 评估是否达到目标
  const TARGET_HEAP = 20 * 1024 * 1024; // 20MB
  const TARGET_RSS = 50 * 1024 * 1024;  // 50MB (RSS 通常比 heap 大)

  if (heapAvg <= TARGET_HEAP) {
    console.log('✅ 堆内存使用达标!平均使用低于 20MB 目标。');
  } else {
    console.log(`⚠️ 堆内存使用未达标。平均 ${formatMemory(heapAvg)},目标 20MB。`);
  }

  if (heapMax - heapMin < 10 * 1024 * 1024) {
    console.log('✅ 内存波动稳定!波动范围小于 10MB。');
  } else {
    console.log(`⚠️ 内存波动较大。范围: ${formatMemory(heapMax - heapMin)}`);
  }
}

/**
 * 主测试流程
 */
async function runTest() {
  console.log('🧪 反重力服务内存优化测试');
  console.log('═'.repeat(50));
  console.log(`目标: 堆内存保持在 ~20MB`);
  console.log(`测试时长: ${TEST_DURATION_MS / 1000} 秒`);
  console.log(`采样间隔: ${SAMPLE_INTERVAL_MS / 1000} 秒`);
  console.log('═'.repeat(50));
  console.log('');

  try {
    await startServer();
    console.log('✅ 服务器已启动\n');
    
    testStartTime = Date.now();
    
    // 设置采样定时器
    const sampleInterval = setInterval(collectMemorySample, SAMPLE_INTERVAL_MS);
    
    // 设置负载模拟定时器
    const loadInterval = setInterval(simulateLoad, REQUEST_INTERVAL_MS);
    
    // 等待测试完成
    await new Promise(resolve => setTimeout(resolve, TEST_DURATION_MS));
    
    // 清理定时器
    clearInterval(sampleInterval);
    clearInterval(loadInterval);
    
    // 最后采集一次
    await collectMemorySample();
    
    // 分析结果
    analyzeResults();
    
  } catch (error) {
    console.error('❌ 测试失败:', error.message);
  } finally {
    stopServer();
    process.exit(0);
  }
}

// 处理进程退出
process.on('SIGINT', () => {
  console.log('\n收到中断信号...');
  stopServer();
  process.exit(0);
});

process.on('SIGTERM', () => {
  stopServer();
  process.exit(0);
});

// 运行测试
runTest();