← 返回首页
🏎️

性能测试:JMeter/Gatling/K6

📂 architecture ⏱ 4 min 709 words

性能测试:JMeter/Gatling/K6

JMeter性能测试

JMeter是Apache开源的性能测试工具,支持多种协议和丰富的测试场景。

// JMeter测试计划配置
@Component
public class JMeterTestPlan {
    
    public TestPlan createAPITestPlan(String baseUrl) {
        TestPlan testPlan = new TestPlan("API性能测试");
        
        // 线程组
        ThreadGroup threadGroup = new ThreadGroup("API请求线程");
        threadGroup.setNumThreads(100);
        threadGroup.setRampUp(60);
        threadGroup.setDuration(600);
        threadGroup.setScheduler(true);
        
        // HTTP请求
        HTTPSampler sampler = new HTTPSampler();
        sampler.setUrl(baseUrl + "/api/data");
        sampler.setMethod("GET");
        sampler.setConnectTimeout(5000);
        sampler.setResponseTimeout(30000);
        
        // 请求头管理器
        HeaderManager headerManager = new HeaderManager();
        headerManager.add(new HTTPSampler.Header("Content-Type", "application/json"));
        headerManager.add(new HTTPSampler.Header("Authorization", "Bearer ${token}"));
        
        // 断言
        ResponseAssertion assertion = new ResponseAssertion();
        assertion.setTestField("response_code");
        assertion.setPattern("200");
        
        // 监听器
        SummaryReport report = new SummaryReport();
        AggregateReport aggregateReport = new AggregateReport();
        ViewResultsFullVisualizer resultsVisualizer = new ViewResultsFullVisualizer();
        
        testPlan.add(threadGroup);
        threadGroup.add(headerManager);
        threadGroup.add(sampler);
        sampler.add(assertion);
        threadGroup.add(report);
        threadGroup.add(aggregateReport);
        threadGroup.add(resultsVisualizer);
        
        return testPlan;
    }
}

// JMeter脚本生成
@Component
public class JMeterScriptGenerator {
    
    public String generateScript(TestConfig config) {
        StringBuilder script = new StringBuilder();
        
        script.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        script.append("<jmeterTestPlan version=\"1.2\">\n");
        script.append("  <hashTree>\n");
        script.append("    <TestPlan guiclass=\"TestPlanGui\" ");
        script.append("testclass=\"TestPlan\" testname=\"Performance Test\">\n");
        
        // 线程组
        script.append("      <elementProp name=\"ThreadGroup.main\" ");
        script.append("elementType=\"ThreadGroup\" ");
        script.append("guiclass=\"ThreadGroupGui\" ");
        script.append("testclass=\"ThreadGroup\" ");
        script.append("testname=\"Thread Group\">\n");
        script.append("        <stringProp name=\"ThreadGroup.num_threads\">");
        script.append(config.getThreadCount()).append("</stringProp>\n");
        script.append("        <stringProp name=\"ThreadGroup.ramp_time\">");
        script.append(config.getRampUpTime()).append("</stringProp>\n");
        script.append("      </elementProp>\n");
        
        script.append("    </TestPlan>\n");
        script.append("  </hashTree>\n");
        script.append("</jmeterTestPlan>\n");
        
        return script.toString();
    }
}

Gatling性能测试

Gatling是基于Scala的高性能测试工具,以代码即测试的理念著称。

// Gatling测试脚本
import io.gatling.core.Predef._
import io.gatling.http.Predef._

class ApiSimulation extends Simulation {

  val httpProtocol = http
    .baseUrl("http://localhost:8080")
    .acceptHeader("application/json")
    .contentTypeHeader("application/json")

  val scn = scenario("API Test")
    .exec(http("Get Data")
      .get("/api/data")
      .check(status.is(200))
      .check(jsonPath("$.result").exists))
    .pause(1)
    .exec(http("Create Data")
      .post("/api/data")
      .body(StringBody("""{"key": "value"}"""))
      .check(status.is(201))
      .check(jsonPath("$.id").saveAs("dataId")))
    .pause(1)
    .exec(http("Get Created Data")
      .get("/api/data/${dataId}")
      .check(status.is(200)))

  setUp(
    scn.inject(
      rampUsers(100).during(60.seconds),
      constantUsersPerSec(10).during(5.minutes)
    )
  ).protocols(httpProtocol)
}
// Gatling高级配置
class AdvancedSimulation extends Simulation {

  // 自定义 feeder
  val userFeeder = Iterator.continually {
    Map(
      "userId" -> java.util.UUID.randomUUID().toString,
      "timestamp" -> System.currentTimeMillis()
    )
  }

  // 自定义检查
  def customCheck(response: io.gatling.http.response.Response): Boolean = {
    response.body.string.exists(_.contains("success"))
  }

  val scn = scenario("Advanced Test")
    .feed(userFeeder)
    .exec(http("Request")
      .get("/api/users/${userId}")
      .check(customCheck _))
    .doIf("#{statusCode == 200}") {
      exec(http("Success")
        .get("/api/success"))
    }

  setUp(
    scn.inject(
      nothingFor(5.seconds),
      atOnceUsers(10),
      rampUsers(100).during(10.seconds),
      constantUsersPerSec(20).during(1.minute),
      rampUsersPerSec(10).to(50).during(2.minutes)
    )
  ).protocols(httpProtocol)
    .assertions(
      global.responseTime.max.lt(1000),
      global.successfulRequests.percent.gt(95.0)
    )
}

K6性能测试

K6是基于Go和JavaScript的现代性能测试工具,专注于开发者体验。

// K6测试脚本
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// 自定义指标
const errorRate = new Rate('errors');
const duration = new Trend('duration');

export const options = {
  stages: [
    { duration: '30s', target: 20 },  // 30秒内增加到20用户
    { duration: '1m', target: 20 },   // 保持20用户1分钟
    { duration: '30s', target: 50 },  // 30秒内增加到50用户
    { duration: '1m', target: 50 },   // 保持50用户1分钟
    { duration: '30s', target: 0 },   // 30秒内减少到0用户
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95%的请求响应时间小于500ms
    http_req_failed: ['rate<0.1'],     // 错误率小于10%
    errors: ['rate<0.1'],
  },
};

export default function () {
  const response = http.get('http://localhost:8080/api/data');
  
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
    'response has data': (r) => r.json().data !== null,
  });
  
  errorRate.add(response.status !== 200);
  duration.add(response.timings.duration);
  
  sleep(1);
}

// 高级配置
export const options = {
  scenarios: {
    // 场景1:基准测试
    baseline: {
      executor: 'constant-vus',
      vus: 10,
      duration: '2m',
      exec: 'baselineTest',
    },
    // 场景2:负载测试
    load: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 100 },
        { duration: '5m', target: 100 },
        { duration: '2m', target: 0 },
      ],
      exec: 'loadTest',
    },
    // 场景3:压力测试
    stress: {
      executor: 'ramping-arrival-rate',
      startRate: 100,
      timeUnit: '1s',
      preAllocatedVUs: 100,
      stages: [
        { duration: '2m', target: 100 },
        { duration: '5m', target: 500 },
        { duration: '2m', target: 100 },
      ],
      exec: 'stressTest',
    },
  },
};

测试工具对比

# 性能测试工具对比
testing_tools:
  jmeter:
    name: "JMeter"
    language: "Java"
    protocol_support:
      - HTTP/HTTPS
      - FTP
      - JDBC
      - SOAP/REST
      - JMS
      - LDAP
    pros:
      - 协议支持广泛
      - GUI友好
      - 插件丰富
      - 社区活跃
    cons:
      - 资源占用高
      - 脚本复杂
      - 分布式测试配置复杂
    best_for: "企业级应用,多协议测试"
  
  gatling:
    name: "Gatling"
    language: "Scala"
    protocol_support:
      - HTTP/HTTPS
      - WebSocket
      - JMS
    pros:
      - 高性能
      - 代码即测试
      - 详细的报告
      - CI/CD集成好
    cons:
      - 学习曲线陡峭
      - 协议支持有限
    best_for: "Web应用,API测试"
  
  k6:
    name: "K6"
    language: "JavaScript"
    protocol_support:
      - HTTP/HTTPS
      - WebSocket
      - gRPC
      - MQTT
    pros:
      - 现代化设计
      - 开发者友好
      - 性能优秀
      - 云原生支持
    cons:
      - 相对较新
      - 企业级功能有限
    best_for: "微服务,云原生应用"

测试报告分析

// 测试报告生成
@Component
public class TestReportGenerator {
    
    public TestReport generateReport(List<TestResult> results) {
        TestReport report = new TestReport();
        
        // 计算关键指标
        double avgResponseTime = results.stream()
            .mapToLong(TestResult::getResponseTime)
            .average()
            .orElse(0);
        
        double p95ResponseTime = results.stream()
            .mapToLong(TestResult::getResponseTime)
            .sorted()
            .skip((long) (results.size() * 0.95))
            .findFirst()
            .orElse(0);
        
        double p99ResponseTime = results.stream()
            .mapToLong(TestResult::getResponseTime)
            .sorted()
            .skip((long) (results.size() * 0.99))
            .findFirst()
            .orElse(0);
        
        long totalRequests = results.size();
        long failedRequests = results.stream()
            .filter(r -> !r.isSuccess())
            .count();
        
        double throughput = totalRequests / 
            (results.get(results.size() - 1).getTimestamp() - 
             results.get(0).getTimestamp()) * 1000;
        
        report.setAvgResponseTime(avgResponseTime);
        report.setP95ResponseTime(p95ResponseTime);
        report.setP99ResponseTime(p99ResponseTime);
        report.setTotalRequests(totalRequests);
        report.setFailedRequests(failedRequests);
        report.setSuccessRate((double) (totalRequests - failedRequests) / totalRequests * 100);
        report.setThroughput(throughput);
        
        return report;
    }
    
    // 性能瓶颈分析
    public List<Bottleneck> analyzeBottlenecks(TestReport report) {
        List<Bottleneck> bottlenecks = new ArrayList<>();
        
        // 响应时间分析
        if (report.getP95ResponseTime() > 1000) {
            bottlenecks.add(Bottleneck.builder()
                .type("响应时间")
                .description("P95响应时间超过1秒")
                .severity("HIGH")
                .suggestion("优化慢查询,增加缓存")
                .build());
        }
        
        // 错误率分析
        if (report.getErrorRate() > 1) {
            bottlenecks.add(Bottleneck.builder()
                .type("错误率")
                .description("错误率超过1%")
                .severity("MEDIUM")
                .suggestion("检查错误日志,修复异常")
                .build());
        }
        
        // 吞吐量分析
        if (report.getThroughput() < 100) {
            bottlenecks.add(Bottleneck.builder()
                .type("吞吐量")
                .description("吞吐量低于100 TPS")
                .severity("MEDIUM")
                .suggestion("优化代码,增加并发处理能力")
                .build());
        }
        
        return bottlenecks;
    }
}

性能测试工具各有特点,JMeter适合企业级多协议测试,Gatling适合Web应用测试,K6适合现代云原生应用,选择时需根据项目需求和技术栈决定。