【K6性能测试】K6学习笔记

什么是 k6

使用 k6,你可以测试应用程序和基础设施的可靠性和性能。

工程团队(包括开发人员、QA 工程师、SDET 和 SRE)通常使用 k6 来:

  • 负载和性能测试
  • 浏览器性能测试
  • 性能和综合监控
  • 性能测试自动化
  • 混沌与弹性测试

安装 K6

K6 有能在多种平台安装:

  • windows
  • mac
  • linux

除此之外,还可以通过 Docker 容器方式运行 k6

目前我用的开发主力机是 mac,在 mac 上安装 k6 的途径主要是通过homebrew

1
brew install  k6

此外就是通过 Docker 安装 k6

1
docker pull grafana/k6

编写第一个 k6 测试脚本

前提条件

要编写 k6 测试脚本,需要以下前提:

  • javascript 或者 typescript 知识
  • 在电脑上安装 k6
  • 用于编写测试脚本的代码编辑器(我目前使用的是 vscode)

k6 测试脚本的结构

每个 k6 脚本都遵循一个通用结构,围绕几个核心组件:

  • 默认函数:这是测试逻辑所在的位置。它定义了测试将执行的操作以及在执行过程中的行为方式。它应作为脚本中的默认函数导出。
  • imports :您可以进口额外的 k6 模块或 JavaScript 库 (jslibs) 用于扩展脚本功能,例如发出 HTTP 请求或模拟浏览器交互。请注意,k6 并非基于 Node.js 构建,而是使用其自身的 JavaScript 运行时。与某些 npm 模块的兼容性可能有所不同。
  • options(可选) :允许您配置测试的执行,例如定义虚拟用户数量、测试时长或设置性能阈值。请参阅 选项以了解更多详细信息。
  • 生命周期操作(可选) :由于您的测试可能需要在执行测试逻辑之前和/或之后运行代码,例如从文件解析数据或从 Amazon S3 下载对象, 生命周期操作允许您编写代码,作为预定义函数或在特定代码范围内,这些代码将在测试执行的不同阶段执行。

编写 k6 测试脚本

  • 创建测试脚本(手动创建一个 js 文件或者使用k6 new test.js)
  • 导入模块(import http from 'k6/http')
  • 定义函数(export default function() {})
  • 声明测试选项(export const options = {})

接下来查看一个完整的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 导入模块
import http from 'k6/http'
import { sleep } from 'k6'

// 定义可选项
export const options = {
  iterations: 10,
}

// The default exported function is gonna be picked up by k6 as the entry point for the test script. It will be executed repeatedly in "iterations" for the whole duration of the test.
// 默认函数,测试脚本逻辑就在这里面,必选项
export default function () {
  // Make a GET request to the target URL
  http.get('https://quickpizza.grafana.com')

  // Sleep for 1 second to simulate real-world usage
  sleep(1)
}

// 测试生命周期函数,可选的

更多的 http 请求方式

k6 支持所有的 http 请求方法

  • get(GET)
  • post(POST)
  • put(PUT)
  • del(DELETE)
  • patch(PATCH)
  • head(HEAD)
  • options(OPTIONS)
  • requests(类似于 python 的 request 库)
  • batch(批量请求)

get 方法

1
2
3
4
5
import http from 'k6/http'

export default function () {
  http.get('http://test.k6.io')
}

post 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import http from 'k6/http'

export default function () {
  const url = 'http://test.k6.io/login'
  const payload = JSON.stringify({
    email: 'aaa',
    password: 'bbb',
  })

  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  }

  http.post(url, payload, params)
}

http 请求标签

标签示例

k6 会自动应用内置标签到 http 请求中,我们可以使用标签来过滤结果并组织分析

以下是一些常用的 http 相关的标签

标签名 描述
expected_response 是否是期望的响应,默认情况下 200-399 之间的都是 true,也可以通过setResponseCallback更改默认行为
group 当请求在某个 group 中时是组名,默认值是空
name 默认为请求的 url
method http 请求方法
scenario 当请求在某个 scenario 中时,标签值是 scenario 名,默认为 default
status 响应状态码
url 默认为 url 请求地址

以下代码展示了如何记录测试结果数据点的 JSON 示例,在此示例中,指标是 HTTP 请求的持续时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "type": "Point",
  "metric": "http_req_duration",
  "data": {
    "time": "2017-06-02T23:10:29.52444541+02:00",
    "value": 586.831127,
    "tags": {
      "expected_response": "true",
      "group": "",
      "method": "GET",
      "name": "http://test.k6.io",
      "scenario": "default",
      "status": "200",
      "url": "http://test.k6.io"
    }
  }
}

使用标签将 URL 分组

默认情况下,标签会有一个name字段,用于保存请求的 URL 的值,如果测试包含动态 URL 路径,我们希望将数据聚合分析,而不是大量的唯一 URL 进入指标流。

1
2
3
4
5
6
7
8
9
import http from 'k6/http'

export default function () {
  for (let id = 1; id <= 100; id++) {
    http.get(`http://example.com/posts/${id}`)
  }
}
// tags.name=\"http://example.com/posts/1\",
// tags.name=\"http://example.com/posts/2\",

我们更希望在单个指标中报告这个数据,要从动态的 URL 聚合数据,我们可以设置 http 请求的name标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import http from 'k6/http'

export default function () {
  for (let id = 1; id <= 100; id++) {
    http.get(`http://example.com/posts/${id}`, {
      tags: { name: 'PostsItemURL' },
    })
  }
}
// tags.name=\"PostsItemURL\",
// tags.name=\"PostsItemURL\",

上面的代码会产生如下的 JSON 输出

 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
// For http://example.com/1, note that the url is not present in the JSON.

{
    "type":"Point",
    "metric":"http_req_duration",
    "data": {
        "time":"2017-06-02T23:10:29.52444541+02:00",
        "value":586.831127,
        "tags": {
            "method":"GET",
            "name":"PostsItemURL",
            "status":"200",
            "url":"PostsItemURL"
        }
    }
}

// and for http://example.com/2

{
    "type":"Point",
    "metric":"http_req_duration",
    "data": {
        "time":"2017-06-02T23:10:29.58582529+02:00",
        "value":580.839273,
        "tags": {
            "method":"GET",
            "name":"PostsItemURL",
            "status":"200",
            "url":"PostsItemURL"
        }
    }
}

尽管两个对象的url不同,但是它们的name却是相同的

或者,你也可以使用http.url包装器来设置具有模版字符串值的name标签

1
2
3
4
5
6
7
8
9
import http from 'k6/http'

export default function () {
  for (let id = 1; id <= 100; id++) {
    http.get(http.url`http://example.com/posts/${id}`)
  }
}
// tags.name="http://example.com/posts/${}",
// tags.name="http://example.com/posts/${}",

指标

指标衡量系统在测试条件下的性能。默认情况下,k6 会自动收集内置指标。除了内置指标外,还可以自定义指标。

指标分类

指标大致分为四类:

  • Counters 计算总和值。
  • Gauges 跟踪最小值、最大值和最新值。
  • Rates 跟踪非零值出现的频率。
  • Trends 计算多个值(如平均值、众数或百分位数)的统计数据。

为了使测试不满足某些标准,你可以编写一个基于指标标准的阈值。

要过滤指标,你可以使用taggroup

还可以导出各种摘要和粒度格式的指标,如Result output

要观测的指标

每个指标都提供了不同的绩效视角,因此,最合适分析的指标取决于自身的目标

如果你不确定要关注哪些指标,则可以从衡量请求、错误和持续时间的指标开始(RED 方法)

  • http_reqs ,用于测量请求
  • http_req_failed ,测量错误率
  • http_req_duration ,用于测量持续时间

换句话说,这些指标衡量流量、可用性和延迟,SRE 可能会将这些指标视为four Golden Signals

示例输出

当你运行测试时,所有内置的和自定义指标的汇总摘要将输出到stdout:

1
2
3
4
5
import http from 'k6/http'

export default function () {
  http.get('https://quickpizza.grafana.com')
}

上述脚本将输出如下内容:

 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
$ k6 run script.js

         /\      Grafana   /‾‾/
    /\  /  \     |\  __   /  /
   /  \/    \    | |/ /  /   ‾‾\
  /          \   |   (  |  ()  |
 / __________ \  |_|\_\  \_____/


  execution: local
     script: http_get.js
     output: -

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)



  █ TOTAL RESULTS

    HTTP
    http_req_duration.......................................................: avg=117.55ms min=117.55ms med=117.55ms max=117.55ms p(90)=117.55ms p(95)=117.55ms
      { expected_response:true }............................................: avg=117.55ms min=117.55ms med=117.55ms max=117.55ms p(90)=117.55ms p(95)=117.55ms
    http_req_failed.........................................................: 0.00%  0 out of 1
    http_reqs...............................................................: 1      2.768749/s

    EXECUTION
    iteration_duration......................................................: avg=361.09ms min=361.09ms med=361.09ms max=361.09ms p(90)=361.09ms p(95)=361.09ms
    iterations..............................................................: 1      2.768749/s

    NETWORK
    data_received...........................................................: 6.8 kB 19 kB/s
    data_sent...............................................................: 541 B  1.5 kB/s

running (00m03.8s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m03.8s/10m0s  1/1 iters, 1 per VU

在该输出中,所有以httpinterationvu开头的指标都是内置指标

指标名称限制

指标名称必须符合 OpenTelemetryPrometheus 的限制 。字符限制与 k6 在限制字符集之前的限制相同

意味着:

  • 包含最多 128 个符号(ASCII 字母、数字或下划线)。
  • 以字母或下划线开头。

内置指标

无论测试什么协议(http/websocket/grpc),k6 始终收集以下指标

指标名 类型 描述
checks Rate 检查成功率
data_received Counter 已接收到的数据
data_send Counter 已发送的数据
dropped_iterations Counter 由于 vu 不足或时间不足而未启动的迭代次数
iteration_duration Trend 完成一次完整的迭代所需的时间,包括setupteardown
iterations Counter vu 执行 js 脚本(default 函数)的总次数
vus Gauge 当前活跃虚拟用户数
vus_max Gauge 最大可能的虚拟用户数

http 内置指标

指标名称 类型 描述
http_req_blocked Trend 发起请求之前阻塞的时间float
http_req_connecting Trend 与远程主机建立 TCP 连接所花费的时间float
http_req_duration Trend 请求的总时间。它等于 http_req_sending + http_req_waiting + http_req_receiving (即远程服务器处理请求和响应所需的时间,不包括初始 DNS 查找/连接时间) float
http_req_failed Rate 根据 setResponseCallback 回调函数定义为失败的请求
http_req_receiving Trend 从远程主机接收响应数据所花费的时间 float
http_req_sending Trend 向远程主机发送数据所花费的时间float
http_req_tls_handshaking Trend 与远程主机握手 TLS 会话所用的时间
http_req_waiting Trend 等待远程主机响应的时间(又称“第一个字节的时间”或“TTFB”) float
http_reqs Counter k6 总共生成了多少个 HTTP 请求

浏览器指标

K6 浏览器指标基于Core Web Vitals

随着技术的变化,这些核心指标也会随之变化,目前,K6 跟踪以下核心网络生命力:

Metric name Description
browser_web_vital_cls 通过量化可见页面内容的意外布局偏移量来衡量网页的视觉稳定性。请参阅Cumulative Layout Shift以获取更多信息。
browser_web_vital_fid 通过量化用户首次交互(例如点击按钮)与浏览器响应之间的延迟来衡量网页的响应能力。请参阅First Input Delay了解更多信息。
browser_web_vital_lcp 测量页面上最大内容元素可见所需的时间。请参阅 请参阅Larger Contentful Paint 以了解更多信息。

除了核心 Web 指标外,浏览器模块还报告Other Web Vitals

指标名称 描述
browser_web_vital_fcp 测量浏览器渲染页面上第一个 DOM 元素(无论是文本、图片还是标题)所需的时间。请参阅First Contentful Paint 了解更多信息
browser_web_vital_inp 衡量页面响应能力的实验性指标请参阅Interaction to Next Paint以获取更多信息.
browser_web_vital_ttfb 测量浏览器请求和服务器响应之间的时间。请参阅Time to First Byte以获取更多信息

内置 Websocket 指标

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计