很多人用k6做性能测试的时候,总觉得指标统计结果和实际情况对不上,要么是延迟算得比实际快,要么是自定义指标把失败请求的时间也加进去了,最后监控数据要么失真,要么用来判断性能的时候完全错了。今天就把k6的内置指标和自定义指标的计算逻辑掰碎了说,让大家以后再也不会踩坑。

一、为什么要搞懂指标计算逻辑

很多开发者用k6的时候,都是直接复制官方示例,跑起来看结果,却从来没深究过这些数字是怎么来的。比如看到http_req_duration的平均值是100ms,就觉得这个接口性能很好,但实际可能是把错误请求的时间也算进去了,或者把DNS解析的时间重复计算了。如果搞懂了计算逻辑,就能避免被这些数字误导,不会误判系统性能,也不会在监控里出现不该有的告警。

二、k6内置指标的真实计算逻辑

内置指标是k6帮我们统计好的常用指标,但很多人不知道,这些指标的计算是有固定规则的,不是简单的“请求时间”。

2.1 常见内置指标的拆分

比如大家最关心的http_req_duration,k6是按请求全流程的时间总和计算的:从准备发送请求的时间,到DNS找服务器的时间,再到建立TCP连接的时间、TLS握手的时间,加上发送请求的时间、等待服务器响应的时间,最后是接收响应的时间,这些环节的时间加起来就是这个指标的值。另外还有http_reqs,这个指标统计的是所有请求,不管状态码是200还是500,只要发出去就算,别误以为是成功请求的数量。

2.2 内置指标容易踩的坑

比如跑性能测试时,有时候http_req_waiting的时间特别长,这其实是服务器处理请求的时间,但如果测试脚本里加了多余的sleep等待,就会把这个时间算进去,导致统计结果不准。还有,http_req_duration会包含重定向的时间,如果你只关心最终接口的性能,重定向的时间也会拉低真实表现,这时候就得用自定义指标来过滤。

// 技术栈:k6 JavaScript脚本示例
import http from 'k6/http';
import { sleep } from 'k6';

// 基础测试脚本,访问常用的百度首页
export default function () {
  const res = http.get('https://www.baidu.com');
  // k6会自动收集内置指标,不需要手动记录,比如http_req_duration、http_reqs等
  sleep(1);
}
// 跑这个脚本时,会生成对应的内置指标数据,注意http_reqs统计的是所有请求,包含状态码异常的请求

这个示例很简单,跑起来后就能直观看到内置指标的变化,但如果要精准统计业务相关的指标,就得用到自定义指标。

三、自定义指标怎么写才不会失真

内置指标虽然方便,但有时候不符合业务需求,比如要统计“成功的商品详情页请求的处理时间”,这时候就得用自定义指标。但很多人写自定义指标时,不加过滤,把失败请求的时间也加进去,导致统计结果比实际高或低,造成失真。

3.1 自定义指标的声明规则

k6提供了四种自定义指标类型,要根据需求选对类型:统计趋势(比如平均值、最大值)用Trend,统计请求次数用Counter,记录瞬时值用Gauge,统计成功率用Rate。比如统计处理时间就选Trend,因为需要计算时间的变化趋势。

3.2 自定义指标的正确计算示例

// 技术栈:k6 JavaScript脚本示例
import http from 'k6/http';
import { Trend, sleep } from 'k6';

// 声明自定义指标:专门统计成功的商品详情页请求的处理时间
const productDetailSuccessTime = new Trend('product_detail_success_duration');

export default function () {
  // 模拟访问具体商品的详情页,参数是商品ID
  const productId = 12345;
  const res = http.get(`https://api.test-shop.com/product/${productId}`);
  
  // 关键过滤逻辑:只把状态码为200的成功请求时间加入指标
  if (res.status === 200) {
    // 把请求的实际处理时间(不含重定向、DNS等环节)加入自定义指标
    productDetailSuccessTime.add(res.timings.duration);
  } else {
    // 单独记录失败请求,方便后续排查问题,不会影响成功请求的统计
    console.log(`商品详情页请求失败,状态码:${res.status},商品ID:${productId}`);
  }
  
  sleep(0.5);
}
// 这个示例的核心是过滤状态码,避免把404、500等错误请求的时间算进去,确保自定义指标只反映真实有效的业务请求性能

如果不做这个过滤,比如有大量商品ID错误的404请求,其响应时间可能只有几十毫秒,会拉低成功请求的平均值,让人误以为接口性能很好,但实际成功请求的时间可能已经达到几百毫秒,这就是典型的监控数据失真。

四、实际应用场景

4.1 性能测试对接监控系统的场景

很多公司会把k6的指标导入Prometheus,再用Grafana做监控面板。比如电商大促前的压测,要是自定义指标没过滤成功请求,面板上显示的商品列表页平均响应时间就不准,运维人员会误以为系统性能足够,错过潜在的性能瓶颈,大促时就可能出现系统崩溃。

4.2 排查接口异常的场景

当某个接口的错误率突然升高时,要先区分是成功请求变慢,还是失败请求的时间异常。如果自定义指标只统计成功请求,就能快速找到问题:比如成功请求的平均时间从100ms升到了500ms,说明接口本身出现了性能问题;如果失败请求的时间异常,可能是上游依赖的服务出了问题,这样排查起来会更高效。

五、技术优缺点和注意事项

5.1 优点

内置指标是k6官方维护的,计算逻辑经过验证,准确可靠,适合快速做基础性能测试;自定义指标灵活,可以完全贴合业务需求,只统计关心的请求,不会被无关的请求干扰,适配不同的业务场景。

5.2 缺点

内置指标的计算逻辑固定,没法满足特殊业务需求,比如要统计“支付成功后回调接口的时间”,内置指标没法直接实现;自定义指标需要自己处理过滤、计算规则,容易出错,比如忘记过滤状态码,或者选了错误的指标类型(比如用Counter来统计时间)。

5.3 注意事项

第一,用内置指标时,要先搞懂每个指标的具体含义,比如http_req_duration是全流程时间,不是单纯的响应时间,避免误解;第二,写自定义指标时,一定要明确业务规则:哪些请求要算、哪些不算,状态码怎么过滤,要不要加标签(比如商品ID、用户ID),标签要统一,方便后续细分统计;第三,不要混合使用内置和自定义指标,比如不要用内置的http_req_duration加上自定义的指标,避免重复计算,导致数据混乱。

六、总结

搞懂k6的内置指标和自定义指标的计算逻辑,是避免监控数据失真的核心。内置指标虽然方便,但要注意它的计算范围,不能直接套用所有业务场景;自定义指标要根据业务需求来设计,过滤掉无关的请求,确保统计的是真实有效的数据。只有把这些基础搞清楚,才能用k6的测试结果准确判断系统性能,不会被错误的监控数据误导,在压测、排查问题时都能得到靠谱的结论。