API/应用响应时间测试

9 阅读2分钟

API/应用响应时间测试

测试响应时间的不同层面

1. HTTP/API响应时间测试

使用curl测试
# 基本响应时间测试
curl -w "\nTotal: %{time_total}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\n" -o /dev/null -s https://api.example.com/endpoint

# 重复测试多次取平均值
for i in {1..10}; do curl -w "%{time_total}\n" -o /dev/null -s https://api.example.com/endpoint; done | awk '{total+=$1} END {print "Average: " total/NR "s"}'
使用ab工具(Apache Benchmark)
# 安装(Windows可通过WSL或直接下载)
# Ubuntu/Debian: apt-get install apache2-utils

# 执行1000次请求,10个并发
ab -n 1000 -c 10 https://api.example.com/endpoint

2. 使用Node.js进行API测试

const axios = require('axios');
const { performance } = require('perf_hooks');

async function testResponseTime() {
  const iterations = 100;
  let totalTime = 0;
  const responseTimes = [];
  
  for (let i = 0; i < iterations; i++) {
    const start = performance.now();
    try {
      await axios.get('https://api.example.com/endpoint');
      const end = performance.now();
      const time = (end - start) / 1000; // 转换为秒
      responseTimes.push(time);
      totalTime += time;
    } catch (error) {
      console.error(`请求失败: ${error.message}`);
    }
  }
  
  responseTimes.sort((a, b) => a - b);
  
  console.log(`平均响应时间: ${(totalTime / iterations).toFixed(4)}s`);
  console.log(`最小响应时间: ${Math.min(...responseTimes).toFixed(4)}s`);
  console.log(`最大响应时间: ${Math.max(...responseTimes).toFixed(4)}s`);
  console.log(`P50响应时间: ${responseTimes[Math.floor(iterations * 0.5)].toFixed(4)}s`);
  console.log(`P95响应时间: ${responseTimes[Math.floor(iterations * 0.95)].toFixed(4)}s`);
  console.log(`P99响应时间: ${responseTimes[Math.floor(iterations * 0.99)].toFixed(4)}s`);
}

testResponseTime();

3. 数据库查询响应时间测试

PostgreSQL查询时间测试

-- 方法1: 使用EXPLAIN ANALYZE
EXPLAIN ANALYZE SELECT * FROM users WHERE created_at > '2023-01-01' LIMIT 100;

-- 方法2: 使用psql的计时功能
\timing on
SELECT * FROM users WHERE created_at > '2023-01-01' LIMIT 100;
\timing off

在Node.js中测试数据库查询时间

async function testDbQueryTime() {
  const startTime = performance.now();
  
  // 使用TypeORM示例
  const users = await userRepository.find({
    where: { created_at: MoreThan('2023-01-01') },
    take: 100
  });
  
  const endTime = performance.now();
  const queryTime = (endTime - startTime) / 1000; // 转换为秒
  
  console.log(`数据库查询耗时: ${queryTime.toFixed(4)}s`);
  return users;
}

4. 使用专业工具进行负载测试

JMeter

  • 下载安装JMeter:Apache JMeter官网
  • 创建测试计划:添加线程组、HTTP请求、监听器
  • 配置参数:线程数、循环次数、请求参数
  • 运行测试并分析结果:响应时间图表、吞吐量、错误率

k6

# 安装k6
# Windows: choco install k6
# 或下载安装包

# 创建测试脚本 test.js
cat > test.js << 'EOF'
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 10 },  // 1分钟内从0增加到10个并发用户
    { duration: '3m', target: 10 },  // 保持10个并发用户3分钟
    { duration: '1m', target: 0 },   // 1分钟内减少到0个并发用户
  ],
  thresholds: {
    'http_req_duration': ['p(95)<500'], // 95%的请求响应时间应小于500ms
  },
};

export default function() {
  const res = http.get('https://api.example.com/endpoint');
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
  sleep(1);
}
EOF

# 运行测试
k6 run test.js

5. NestJS应用中的响应时间测试

使用中间件测量响应时间

// src/common/response-time.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class ResponseTimeMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const start = Date.now();
    
    res.on('finish', () => {
      const time = Date.now() - start;
      console.log(`[${req.method}] ${req.url} - ${time}ms`);
      
      // 可以记录到日志系统或监控系统
      // 也可以设置阈值报警
      if (time > 500) {
        console.warn(`⚠️  慢请求警告: ${req.method} ${req.url} - ${time}ms`);
      }
    });
    
    next();
  }
}

在app.module中使用中间件

// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ResponseTimeMiddleware } from './common/response-time.middleware';

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(ResponseTimeMiddleware)
      .forRoutes('*'); // 应用到所有路由
  }
}

6. 监控与分析工具

使用Prometheus + Grafana监控

// NestJS中集成Prometheus
import { PrometheusModule } from '@willsoto/nestjs-prometheus';

@Module({
  imports: [
    PrometheusModule.register(),
    // 其他模块
  ],
})
export class AppModule {}

使用New Relic或Datadog进行APM监控

  • 安装对应SDK
  • 配置监控参数
  • 查看实时响应时间图表和报警

7. 测试最佳实践

  1. 基准测试:先建立性能基准,再进行优化
  2. 隔离测试:分别测试网络、数据库、应用逻辑等不同环节
  3. 负载测试:模拟真实用户负载,观察系统表现
  4. 持续监控:设置长期监控,观察性能趋势
  5. 多维度指标:同时关注响应时间、吞吐量、错误率等指标
  6. 分位数分析:关注P50/P95/P99等不同分位数的响应时间
  7. 自动化测试:将性能测试集成到CI/CD流程中

8. 示例:综合测试脚本

// 完整的API测试脚本(支持GET、POST请求和请求头设定)
const axios = require('axios');
const { performance } = require('perf_hooks');
const fs = require('fs');

async function runLoadTest(baseUrl, endpoints, concurrentUsers, requestsPerUser) {
  const results = {};
  
  for (const endpoint of endpoints) {
    // 支持字符串形式的endpoint(默认GET)和对象形式的endpoint(可指定method、path、data和headers)
    const method = endpoint.method || 'GET';
    const path = endpoint.path || endpoint;
    const data = endpoint.data || null;
    const headers = endpoint.headers || {};
    
    console.log(`测试端点: ${method} ${path}`);
    const url = `${baseUrl}${path}`;
    const allTimes = [];
    const errors = [];
    
    // 模拟并发用户
    const promises = [];
    for (let u = 0; u < concurrentUsers; u++) {
      promises.push((async () => {
        for (let r = 0; r < requestsPerUser; r++) {
          const start = performance.now();
          try {
            // 构建axios请求配置,包含headers
            const config = { headers };
            
            // 根据method决定使用GET还是POST
            if (method.toUpperCase() === 'POST') {
              // POST请求,发送data数据和headers
              await axios.post(url, data, config);
            } else {
              // 其他请求默认为GET,使用headers
              await axios.get(url, config);
            }
            const end = performance.now();
            allTimes.push(end - start);
          } catch (error) {
            errors.push(error.message);
          }
          // 随机延迟模拟真实用户行为
          await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
        }
      })());
    }
    
    await Promise.all(promises);
    
    // 计算统计数据
    allTimes.sort((a, b) => a - b);
    const totalRequests = allTimes.length + errors.length;
    
    // 使用method+path作为结果对象的key
    const resultKey = `${method} ${path}`;
    results[resultKey] = {
      method,
      path,
      hasHeaders: Object.keys(headers).length > 0,
      totalRequests,
      successfulRequests: allTimes.length,
      errorCount: errors.length,
      errorRate: (errors.length / totalRequests * 100).toFixed(2) + '%',
      avgResponseTime: (allTimes.reduce((a, b) => a + b, 0) / allTimes.length).toFixed(2) + 'ms',
      minResponseTime: Math.min(...allTimes).toFixed(2) + 'ms',
      maxResponseTime: Math.max(...allTimes).toFixed(2) + 'ms',
      p50: allTimes[Math.floor(allTimes.length * 0.5)].toFixed(2) + 'ms',
      p95: allTimes[Math.floor(allTimes.length * 0.95)].toFixed(2) + 'ms',
      p99: allTimes[Math.floor(allTimes.length * 0.99)].toFixed(2) + 'ms',
    };
    
    console.log(results[endpoint]);
  }
  
  // 保存结果到文件
  fs.writeFileSync('test-results.json', JSON.stringify(results, null, 2));
  console.log('测试结果已保存到 test-results.json');
}

// 运行测试(示例包含GET和POST请求)
runLoadTest(
  'http://localhost:3000',
  [
    // GET请求示例(字符串形式,向后兼容)
    '/square/posts', 
    '/square/hot-posts?limit=10', 
    '/chat/userChatMenu',
    // POST请求示例(对象形式)
      {
        method: 'POST',
        path: '/auth/login',
        data: {
          username: 'testuser',
          password: 'testpassword'
        }
      },
      {
        method: 'POST', 
        path: '/chat/createChat',
        data: {
          title: '测试对话',
          userId: 1
        },
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer test-token'
        }
      },
      // GET请求带自定义请求头示例
      {
        method: 'GET',
        path: '/user/profile',
        headers: {
          'Authorization': 'Bearer test-token',
          'Accept': 'application/json'
        }
      }
  ],
  10,  // 并发用户数
  50   // 每用户请求数
);

通过这些方法,您可以全面测试和监控API响应时间,确保系统性能满足要求,并及时发现和解决性能瓶颈。