#!/usr/bin/env node const http = require('http'); const https = require('https'); const fs = require('fs'); const path = require('path'); const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080'; const RESULTS_FILE = path.join(__dirname, '../performance-test-results.json'); class PerformanceTester { constructor(baseUrl) { this.baseUrl = baseUrl; this.results = []; } async testEndpoint(endpoint, method = 'GET', body = null) { const url = `${this.baseUrl}${endpoint}`; const startTime = Date.now(); return new Promise((resolve, reject) => { const options = { method: method, headers: { 'Content-Type': 'application/json', } }; if (body) { options.headers['Content-Length'] = Buffer.byteLength(JSON.stringify(body)); } const protocol = url.startsWith('https') ? https : http; const req = protocol.request(url, options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { const endTime = Date.now(); const duration = endTime - startTime; resolve({ endpoint, method, statusCode: res.statusCode, duration, success: res.statusCode >= 200 && res.statusCode < 300, dataSize: data.length }); }); }); req.on('error', (error) => { const endTime = Date.now(); const duration = endTime - startTime; resolve({ endpoint, method, statusCode: 0, duration, success: false, error: error.message }); }); if (body) { req.write(JSON.stringify(body)); } req.end(); }); } async runLoadTest(endpoint, concurrentRequests = 10, totalRequests = 100) { console.log(`\n📊 开始负载测试: ${endpoint}`); console.log(` 并发数: ${concurrentRequests}`); console.log(` 总请求数: ${totalRequests}\n`); const results = []; const startTime = Date.now(); for (let i = 0; i < totalRequests; i += concurrentRequests) { const batch = Math.min(concurrentRequests, totalRequests - i); const promises = []; for (let j = 0; j < batch; j++) { promises.push(this.testEndpoint(endpoint)); } const batchResults = await Promise.all(promises); results.push(...batchResults); console.log(` 进度: ${Math.min(i + batch, totalRequests)}/${totalRequests} 请求已完成`); } const endTime = Date.now(); const totalDuration = endTime - startTime; const successfulRequests = results.filter(r => r.success); const failedRequests = results.filter(r => !r.success); const durations = successfulRequests.map(r => r.duration); const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0; const minDuration = durations.length > 0 ? Math.min(...durations) : 0; const maxDuration = durations.length > 0 ? Math.max(...durations) : 0; const p95Duration = this.calculatePercentile(durations, 95); const p99Duration = this.calculatePercentile(durations, 99); const throughput = (successfulRequests.length / totalDuration) * 1000; return { endpoint, concurrentRequests, totalRequests, successfulRequests: successfulRequests.length, failedRequests: failedRequests.length, successRate: (successfulRequests.length / totalRequests * 100).toFixed(2), totalDuration, avgDuration, minDuration, maxDuration, p95Duration, p99Duration, throughput: throughput.toFixed(2), results }; } calculatePercentile(values, percentile) { if (values.length === 0) return 0; const sorted = [...values].sort((a, b) => a - b); const index = Math.ceil((percentile / 100) * sorted.length) - 1; return sorted[Math.max(0, index)]; } async runPerformanceTests() { console.log('🚀 开始性能测试...\n'); const endpoints = [ { path: '/api/auth/login', method: 'POST', body: { username: 'admin', password: 'admin123' } }, { path: '/api/users', method: 'GET' }, { path: '/api/roles', method: 'GET' }, { path: '/api/menus', method: 'GET' }, { path: '/api/dicts', method: 'GET' }, ]; for (const endpoint of endpoints) { console.log(`\n📡 测试端点: ${endpoint.method} ${endpoint.path}`); const results = []; const iterations = 10; for (let i = 0; i < iterations; i++) { const result = await this.testEndpoint(endpoint.path, endpoint.method, endpoint.body); results.push(result); console.log(` ${i + 1}/${iterations}: ${result.duration}ms - ${result.success ? '✅' : '❌'}`); } const durations = results.filter(r => r.success).map(r => r.duration); const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0; const minDuration = durations.length > 0 ? Math.min(...durations) : 0; const maxDuration = durations.length > 0 ? Math.max(...durations) : 0; const successRate = (results.filter(r => r.success).length / results.length * 100).toFixed(2); this.results.push({ endpoint: endpoint.path, method: endpoint.method, avgDuration, minDuration, maxDuration, successRate, status: this.evaluatePerformance(avgDuration) }); } this.saveResults(); this.printSummary(); } evaluatePerformance(avgDuration) { if (avgDuration < 100) { return '🟢 优秀'; } else if (avgDuration < 300) { return '🟡 良好'; } else if (avgDuration < 500) { return '🟠 一般'; } else { return '🔴 需要优化'; } } saveResults() { const timestamp = new Date().toISOString(); const data = { timestamp, performanceTests: this.results, loadTests: this.loadTestResults }; const history = []; if (fs.existsSync(RESULTS_FILE)) { try { history.push(...JSON.parse(fs.readFileSync(RESULTS_FILE, 'utf8'))); } catch (e) { console.warn('⚠️ 无法解析历史结果文件'); } } history.push(data); if (history.length > 20) { history.shift(); } fs.writeFileSync(RESULTS_FILE, JSON.stringify(history, null, 2)); } printSummary() { console.log('\n📊 性能测试摘要:'); console.log('═══════════════════════════════════════'); const table = this.results.map(r => ({ 端点: r.endpoint, 方法: r.method, 平均: `${r.avgDuration.toFixed(0)}ms`, 最小: `${r.minDuration}ms`, 最大: `${r.maxDuration}ms`, 成功率: `${r.successRate}%`, 状态: r.status })); console.table(table); if (this.loadTestResults) { console.log('\n📈 负载测试摘要:'); console.log('═══════════════════════════════════════'); const loadTable = this.loadTestResults.map(r => ({ 端点: r.endpoint, 总请求: r.totalRequests, 成功: r.successfulRequests, 失败: r.failedRequests, 成功率: `${r.successRate}%`, 平均响应: `${r.avgDuration.toFixed(0)}ms`, P95: `${r.p95Duration.toFixed(0)}ms`, P99: `${r.p99Duration.toFixed(0)}ms`, 吞吐量: `${r.throughput} req/s` })); console.table(loadTable); } console.log('\n💡 性能优化建议:'); this.printRecommendations(); } printRecommendations() { const slowEndpoints = this.results.filter(r => r.avgDuration > 300); if (slowEndpoints.length > 0) { console.log(' ⚠️ 以下端点响应时间较长,建议优化:'); slowEndpoints.forEach(r => { console.log(` - ${r.endpoint}: ${r.avgDuration.toFixed(0)}ms`); }); } const lowSuccessRate = this.results.filter(r => parseFloat(r.successRate) < 95); if (lowSuccessRate.length > 0) { console.log(' ⚠️ 以下端点成功率较低,建议检查:'); lowSuccessRate.forEach(r => { console.log(` - ${r.endpoint}: ${r.successRate}%`); }); } if (slowEndpoints.length === 0 && lowSuccessRate.length === 0) { console.log(' ✅ 所有端点性能良好,无需优化'); } } async runLoadTests() { console.log('\n📊 开始负载测试...\n'); const endpoints = ['/api/users', '/api/roles', '/api/menus']; this.loadTestResults = []; for (const endpoint of endpoints) { const result = await this.runLoadTest(endpoint, 10, 100); this.loadTestResults.push(result); console.log(`\n📈 ${endpoint} 负载测试结果:`); console.log(` 成功率: ${result.successRate}%`); console.log(` 平均响应时间: ${result.avgDuration.toFixed(0)}ms`); console.log(` P95响应时间: ${result.p95Duration.toFixed(0)}ms`); console.log(` P99响应时间: ${result.p99Duration.toFixed(0)}ms`); console.log(` 吞吐量: ${result.throughput} req/s`); } this.saveResults(); } } async function main() { const tester = new PerformanceTester(API_BASE_URL); const command = process.argv[2]; switch (command) { case 'performance': await tester.runPerformanceTests(); break; case 'load': await tester.runLoadTests(); break; case 'all': await tester.runPerformanceTests(); await tester.runLoadTests(); break; default: console.log('使用方法:'); console.log(' node scripts/performance-test.js performance - 运行性能测试'); console.log(' node scripts/performance-test.js load - 运行负载测试'); console.log(' node scripts/performance-test.js all - 运行所有测试'); console.log('\n环境变量:'); console.log(' API_BASE_URL - API基础URL (默认: http://localhost:8080)'); } } if (require.main === module) { main(); } module.exports = PerformanceTester;