Đây là bài viết số 3 trong series K6 Performance Testing:
Trước đó chúng ta đã tìm hiểu và thực hiện các bài kiểm thử dựa trên công cụ K6. Sau mỗi lần chạy kiểm thử, K6 sẽ hiển thị kết quả trực tiếp trên console dưới dạng văn bản thô (plaintext) cùng các chỉ số phân tích khác nhau. Hiểu được kết quả của một bài kiểm thử là một bước để xác nhận rằng liệu ứng dụng được kiểm thử đã thực sự vượt qua các yêu cầu phi chức năng hay chưa. Nếu bạn thấy kết quả hiển thị trên console chưa thực sự trực quan, K6 có thể cung cấp các cách trích xuất và lưu lại kết quả ở các định dạng khác nhau cho mục đích phân tích, báo cáo.
Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu:
- Ý nghĩa của các thông tin trong kết quả trả về của K6
- Các định dạng xuất báo cáo mà K6 hỗ trợ như JSON, CSV, Prometheus,…
Lưu ý: trong bài viết sẽ sử dụng từ khóa output để thay thế cho cụm từ “kết quả”.
Bảng thuật ngữ
Thuật ngữ | Giải thích |
---|---|
Output | Kết quả của bài kiểm thử bởi K6 |
Built-in | Đề cập tới tính năng, thành phần có sẵn trong công cụ K6 |
Metrics | Thông số hoạt động, kết quả của cuộc kiểm thử |
Thông tin trong K6 Output
Dưới đây là một output đã được tạo ra từ bài kiểm tra ở bài viết K6 Performance Testing - Nhập môn:
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: first_test.js
output: -
scenarios: (100.00%) 1 scenario, 50 max VUs, 3m30s max duration (incl. graceful stop):
* default: 50 looping VUs for 3m0s (gracefulStop: 30s)
✓ status is 201
checks.........................: 100.00% 7122 out of 7122
data_received..................: 5.7 MB 31 kB/s
data_sent......................: 800 kB 4.4 kB/s
http_req_blocked...............: avg=794.73µs min=90ns med=510ns max=190.69ms p(90)=922ns p(95)=1.16µs
http_req_connecting............: avg=344.43µs min=0s med=0s max=86.06ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=266.7ms min=221.4ms med=240.35ms max=785.97ms p(90)=303.18ms p(95)=314.48ms
{ expected_response:true }...: avg=266.7ms min=221.4ms med=240.35ms max=785.97ms p(90)=303.18ms p(95)=314.48ms
http_req_failed................: 0.00% 0 out of 7122
http_req_receiving.............: avg=631.32µs min=17.03µs med=82.09µs max=432.25ms p(90)=138.69µs p(95)=174.29µs
http_req_sending...............: avg=74.19µs min=18.96µs med=69.29µs max=3.48ms p(90)=114.19µs p(95)=133.43µs
http_req_tls_handshaking.......: avg=347.82µs min=0s med=0s max=90.19ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=266ms min=220.94ms med=240.15ms max=767.16ms p(90)=302.57ms p(95)=312.99ms
http_reqs......................: 7122 39.291372/s
iteration_duration.............: avg=1.26s min=1.22s med=1.24s max=1.94s p(90)=1.3s p(95)=1.31s
iterations.....................: 7122 39.291372/s
vus............................: 16 min=16 max=50
vus_max........................: 50 min=50 max=50
running (3m01.3s), 00/50 VUs, 7122 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 3m0s
Output của chúng ta được hiển thị theo định dạng mặc định trên console, bao gồm các dòng thông tin. Từng dòng một, chúng ta cùng nhau phân tích!
Các tham số cấu hình
5 dòng đầu tiên không bao gồm hình ảnh logo của K6 là những giá trị tham số cấu hình của bài kiểm thử.
execution: local
Tham số execution
chỉ ra môi trường chạy kịch bản test. Vì chúng ta đang sử dụng bản K6 OSS (open source) nên giá trị hiển thị là local
, nghĩa là kịch bản của test được thực thi trên máy người dùng. Đối với bản K6 Cloud, bạn sẽ thấy giá trị hiển thị là cloud
.
script: first_test.js
Một tham số dễ hiểu, đây là tên tệp tin chứa kịch bản đã được thực thi.
output: -
Ký tự -
chỉ ra hành vi mặc định về định dạng hiển thị kết quả. Ở phần sau của bài viết, chúng ta sẽ lưu trữ kết quả ở các định dạng khác nhau và cùng theo dõi giá trị của tham số output
.
scenarios: (100.00%) 1 scenario, 50 max VUs, 3m30s max duration (incl. graceful stop):
Một scenario(kịch bản) là một tập các hướng dẫn về việc chạy kịch bản kiểm thử: đoạn code nào sẽ chạy, khi nào và tần suất đoạn code sẽ chạy, … Trong trường hợp của chúng ta, có:
- Một scenario được cấu hình trong bài test
- Tối đa 50 virtual users (VUs) - mỗi VU giả lập một người dùng chạy các vòng lặp kịch bản một cách đồng thời và riêng biệt, tương đương một người dùng thực của ứng dụng.
- Thời gian chạy tối đa 3 phút 30 giây, trong đó, mặc định K6 sẽ sử dụng 30 giây cho hoạt động graceful stop - khoảng thời gian kết thúc của bài kiểm thử khi k6 hoàn thành các vòng lặp.
* default: 50 looping VUs for 3m0s (gracefulStop: 30s)
default
đề cập tới tên mặc định cho scenario. Bởi vì chúng ta không có thiết lập về tên của kịch bản trong bài kiểm thử, K6 sử dụng giá trị mặc định.- iteration là một lần thực thi trong vòng lặp của bài kiểm thử. Trong load test, để giả lập lượng yêu cầu lớn, các request sẽ được thực thi trong một khoảng thời gian và lặp lại liên tục.
Kết quả tổng quan
Phần thông tin tiếp theo sẽ cho chúng ta biết về kết quả sơ bộ của bài kiểm thử.
✓ status is 201
Hãy nhìn lại một đoạn code trước khi giải thích chi tiết giá trị trên:
check(response, {
'status is 201': (r) => r.status === 201,
});
Điều kiện mà chúng ta đưa vào trong bài kiểm thử là các request HTTP cần phản hồi với mã trạng thái 201. Kết quả kiểm thử của chúng ta đã xác nhận rằng check là hợp lệ, tương ứng là các request phát sinh đã thỏa mãn điều kiện.
checks.........................: 100.00% 7122 out of 7122
100%, tương ứng 7122 requests đáp ứng yêu cầu check.
K6 built-in metrics
Metrics là những thông số về hoạt động của K6 trong quá trình thực thi kiểm thử hiệu năng. Kết quả của một bài kiểm thử cho biết rất nhiều các metrics hữu ích.
data_received..................: 5.7 MB 31 kB/s
data_sent......................: 800 kB 4.4 kB/s
http_req_blocked...............: avg=794.73µs min=90ns med=510ns max=190.69ms p(90)=922ns p(95)=1.16µs
http_req_connecting............: avg=344.43µs min=0s med=0s max=86.06ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=266.7ms min=221.4ms med=240.35ms max=785.97ms p(90)=303.18ms p(95)=314.48ms
{ expected_response:true }...: avg=266.7ms min=221.4ms med=240.35ms max=785.97ms p(90)=303.18ms p(95)=314.48ms
http_req_failed................: 0.00% 0 out of 7122
http_req_receiving.............: avg=631.32µs min=17.03µs med=82.09µs max=432.25ms p(90)=138.69µs p(95)=174.29µs
http_req_sending...............: avg=74.19µs min=18.96µs med=69.29µs max=3.48ms p(90)=114.19µs p(95)=133.43µs
http_req_tls_handshaking.......: avg=347.82µs min=0s med=0s max=90.19ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=266ms min=220.94ms med=240.15ms max=767.16ms p(90)=302.57ms p(95)=312.99ms
http_reqs......................: 7122 39.291372/s
iteration_duration.............: avg=1.26s min=1.22s med=1.24s max=1.94s p(90)=1.3s p(95)=1.31s
iterations.....................: 7122 39.291372/s
Dưới đây một những metrics có thể nói là quan trọng nhất cho quá trình phân tích bài test.
Thời gian phản hồi - Response time
Thời gian phản hồi là khoảng thời gian từ khi một request được gửi đi cho đến khi nhận được kết quả phản hồi. Một đoạn thời gian, nhưng trong đó là một loạt các hành động diễn ra:
- Kết nối - tls_handshake: hành động mở kết nối tới máy chủ đích, sau khi kết nối được thiết lập, nó sẽ duy trì cho các request ở những lần tiếp theo.
- Gửi - send: K6 tạo và gửi đi yêu cầu tới máy chủ tiếp nhận.
- Chờ - wait: khoảng thời gian mà máy chủ xử lý và gửi ra phản hồi về K6
- Nhận - receive: K6 nhận & xử lý phản hồi từ máy chủ.
Trong hầu hết tình huống, http_req_duration
là metric mà chúng ta tìm kiếm:
http_req_duration..............: avg=266.7ms min=221.4ms med=240.35ms max=785.97ms p(90)=303.18ms p(95)=314.48ms
Và cả những chi tiết cho các thành phần thời gian phản hồi khác, chúng ta cũng sẽ thấy trong kết quả:
http_req_receiving.............: avg=631.32µs min=17.03µs med=82.09µs max=432.25ms p(90)=138.69µs p(95)=174.29µs
http_req_sending...............: avg=74.19µs min=18.96µs med=69.29µs max=3.48ms p(90)=114.19µs p(95)=133.43µs
http_req_tls_handshaking.......: avg=347.82µs min=0s med=0s max=90.19ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=266ms min=220.94ms med=240.15ms max=767.16ms p(90)=302.57ms p(95)=312.99ms
Mỗi dòng đều có chung một định dạng:
avg
: thời gian phản hồi trung bình của tất cả request, bao gồm cả các request thành công và thất bại.min
: thời gian phản hồi nhanh nhất của một requestmed
: thời gian phản hồi trung vị của tất cả request, bao gồm cả các request thành công và thất bại.max
: thời gian phản hồi chậm nhất của một requestp(90)
: thời gian phản hồi của 90% lượng requests, ví dụp(90)=303.18ms
có nghĩa rằng 90% lượng requests có thời gian phản hồi ít hơn hoặc bằng 303.18 mili giây.p(95)
: thời gian phản hồi của 95% lượng requests, ví dụp(95)=314.48ms
có nghĩa rằng 95% lượng requests có thời gian phản hồi ít hơn hoặc bằng 314.48 mili giây.
Bài test K6 không chỉ gửi đi chỉ một request duy nhất, mà có rất nhiều requests được gửi đi. Thời gian phản hồi của những request này là biến thiên, có những request nhận phản hồi nhanh, có những request nhận phản hồi chậm, và có cả những request không nhận được phản hồi. Các giá trị p(xx)
thể hiện mức thời gian phản hồi cho một lượng xx
requests. Giá trị này sẽ hữu ích hơn so với avg
và med
khi chúng ta mô tả yêu cầu hệ thống rằng: xx
tỉ lệ người dùng cần được đảm bảo thời gian phản hồi không quá lâu.
Tỉ lệ lỗi - Error rate
Tỉ lệ lỗi là số lượng request không thành công trong tổng số requests phát sinh trong bài test, đơn vị %. Trong K6, các HTTP response code trong khoảng 200 tới 300 được ghi nhận cho các request thành công. Điều này có nghĩa các mã 4xx và 5xx trong các phản hồi trả về bởi máy chủ sẽ được coi là lỗi. Tuy nhiên, nếu các HTTP response code của bạn khác với các giá trị mặc định của K6, hãy sử dụng tính năng **setResponseCallback(**https://grafana.com/docs/k6/latest/javascript-api/k6-http/set-response-callback/).
http_req_failed................: 0.00% 0 out of 7122
Trong kết quả của chúng ta, một kết quả đẹp với 0% tỉ lệ lỗi, hay nói các khác, tất cả các request đều có phản hồi thành công.
Số lượng request - Number of requests
http_reqs......................: 7122 39.291372/s
7112
là tổng số lượng requests được gửi đi bởi tất cả virtual users trong quá trình test. Thêm nữa, chúng ta có thông tin về số lượng requests trên 1 giây (requests per second - rps): 39.291372
requests trên 1 giây. Giá trị này giúp bạn định lượng về lượng tải mà hệ thống của bạn đã trải qua, góp phần giúp bạn xác định ngưỡng chịu tải của hệ thống.
Thời lượng mỗi vòng lặp - Iteration duration
iteration_duration.............: avg=1.26s min=1.22s med=1.24s max=1.94s
Dòng thông tin này đưa cho chúng ta những lượng thời gian để hoàn thành một vòng lặp. Về định dạng, các giá trị avg, min, med, max tương tự như các giá trị đo lường thời gian http_req_duration
, http_req_sending
,…
Một vòng lặp sẽ phức tạp hơn so với một một HTTP request. Ví dụ, một vòng lặp gồm các bước đăng nhập, tìm kiếm sản phẩm, thêm sản phẩm vào giỏ hàng, tiến thành thanh toán, nhập thông tin thanh toán, xác nhận đặt hàng. Hi vọng ví dụ trên gợi ý cho bạn rằng, người dùng sẽ cần một loạt các thao tác để hoàn tất một chức năng trong ứng dụng thay vì chỉ gửi duy nhất 1 HTTP request. Khi áp dụng điều này, chúng ta sẽ có một kịch bản kiểm tra cho đầy đủ luồng của một chức năng trong hệ thống.
Số lượng vòng lặp - Number of iteration
iterations.....................: 7122 39.291372/s
Trong bài kiểm thử của chúng ta, có 7122
vòng lặp cho kịch bản, con số này tính chung cho tất của các virtual users. Số 39.291372/s
là số lượng vòng lặp thực hiện trong 1 giây. Tương tự như số lượng requests trên 1 giây, số lượng vòng lặp trong 1 giây nói về số lượng hành vi người dùng ứng dụng có thể đạt được trong thời gian 1 giây.
Tổng quan quá trình thực thi
running (3m01.3s), 00/50 VUs, 7122 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 3m0s
Đây là tổng quan những gì đã xảy ra trong bài test.
- Thời gian bài test kéo dài 3 phút 1.3 giây
- 50 virtual user đã hoàn tất các vòng lặp và ngừng chạy khi kết thúc bài test.
- 7122 vòng lặp đã hoàn thành, trong đó không xuất hiện vòng lặp lỗi.
- default scenario đã thực thi thành công với 50 virtual users trong 3 phút.
Output formats
Trong quá trình kịch bản kiểm thử thực thi, K6 luôn liên tục phát ra metrics ở mỗi điểm của bài test. Dựa trên metrics này, có 2 phân loại chính trong K6 output:
- end-of-test: các metrics được tổng hợp lại và kết quả được hình thành ở cuối bài test. Dạng output này chúng ta đã bắt gặp ở phần trước, kết quả tổng quan được hiển thị ở console.
- realtime: các metrics được lưu trữ theo thời gian(timestamp), là nguồn dữ liệu xây dựng nên các biểu đồ theo thời gian thực trên các ứng dụng tích hợp như Prometheus, Grafana,…
Trong bài viết này, chúng ta đề cập tới 2 định dạng kết quả của end-of-test: CSV và JSON.
CSV
Kết quả của cuộc kiểm thử lưu trữ trong tệp tin CSV cho phép hiển thị dữ liệu trực quan thông qua các ứng dụng như Google Sheets, Excel,… Đồng thời, dữ liệu có cấu trúc trong CSV làm đơn giản hóa quá trình đọc và phân tích kết quả.
Để lưu trữ kết quả của bài test ở định dạng tệp tin CSV, hãy sử dụng thêm option --out
trong câu lệnh khởi động bài kiểm thử như sau:
k6 run first_test.js --out csv=results.csv
Bạn có thể sử dụng option ở định dạng rút gọn -o
thay cho --out
.
Sau khi bài test hoàn tất, bạn sẽ nhận được tệp tin results.csv
, chứa nội dung văn bản tương tự như sau:
metric_name,timestamp,metric_value,check,error,error_code,expected_response,group,method,name,proto,scenario,service,status,subproto,tls_version,url,extra_tags,metadata
http_reqs,1740503694,1.000000,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_duration,1740503694,230.209464,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_blocked,1740503694,97.038177,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_connecting,1740503694,32.280113,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_tls_handshaking,1740503694,32.649930,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_sending,1740503694,0.095231,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
http_req_waiting,1740503694,229.992251,,,,true,,POST,https://jsonplaceholder.typicode.com/posts,HTTP/2.0,default,,201,,tls1.3,https://jsonplaceholder.typicode.com/posts,,
Mỗi dòng trong kết quả đại diện cho một metric và các giá trị theo thời gian.
Nếu chúng ta mở tệp tin csv bằng excel, dữ liệu kiểm thị sẽ trực quan hơn.
JSON
Để lưu trữ kết quả tổng quan của bài kiểm thử ở định dạng tệp tin JSON, hãy sử dụng thêm option --out
trong câu lệnh khởi động của K6 như sau:
k6 run first_test.js --out json=results.json
Nội dung tệp tin JSON sẽ tương tự như sau:
{"type":"Metric","data":{"name":"http_reqs","type":"counter","contains":"default","thresholds":[],"submetrics":null},"metric":"http_reqs"}
{"metric":"http_reqs","type":"Point","data":{"time":"2025-06-13T14:30:02.393739661+07:00","value":1,"tags":{"expected_response":"true","group":"","method":"POST","name":"https://jsonplaceholder.typicode.com/posts","proto":"HTTP/2.0","scenario":"default","status":"201","tls_version":"tls1.3","url":"https://jsonplaceholder.typicode.com/posts"}}}
{"type":"Metric","data":{"name":"http_req_duration","type":"trend","contains":"time","thresholds":["p(95)<500"],"submetrics":[{"name":"http_req_duration{expected_response:true}","suffix":"expected_response:true","tags":{"expected_response":"true"}}]},"metric":"http_req_duration"}
{"metric":"http_req_duration","type":"Point","data":{"time":"2025-06-13T14:30:02.393739661+07:00","value":248.205248,"tags":{"expected_response":"true","group":"","method":"POST","name":"https://jsonplaceholder.typicode.com/posts","proto":"HTTP/2.0","scenario":"default","status":"201","tls_version":"tls1.3","url":"https://jsonplaceholder.typicode.com/posts"}}}
{"type":"Metric","data":{"name":"http_req_blocked","type":"trend","contains":"time","thresholds":[],"submetrics":null},"metric":"http_req_blocked"}
{"metric":"http_req_blocked","type":"Point","data":{"time":"2025-06-13T14:30:02.393739661+07:00","value":74.821556,"tags":{"expected_response":"true","group":"","method":"POST","name":"https://jsonplaceholder.typicode.com/posts","proto":"HTTP/2.0","scenario":"default","status":"201","tls_version":"tls1.3","url":"https://jsonplaceholder.typicode.com/posts"}}}
{"type":"Metric","data":{"name":"http_req_connecting","type":"trend","contains":"time","thresholds":[],"submetrics":null},"metric":"http_req_connecting"}
{"metric":"http_req_connecting","type":"Point","data":{"time":"2025-06-13T14:30:02.393739661+07:00","value":28.75016,"tags":{"expected_response":"true","group":"","method":"POST","name":"https://jsonplaceholder.typicode.com/posts","proto":"HTTP/2.0","scenario":"default","status":"201","tls_version":"tls1.3","url":"https://jsonplaceholder.typicode.com/posts"}}}
{"type":"Metric","data":{"name":"http_req_tls_handshaking","type":"trend","contains":"time","thresholds":[],"submetrics":null},"metric":"http_req_tls_handshaking"}
{"metric":"http_req_tls_handshaking","type":"Point","data":{"time":"2025-06-13T14:30:02.393739661+07:00","value":32.372326,"tags":{"expected_response":"true","group":"","method":"POST","name":"https://jsonplaceholder.typicode.com/posts","proto":"HTTP/2.0","scenario":"default","status":"201","tls_version":"tls1.3","url":"https://jsonplaceholder.typicode.com/posts"}}}
Mỗi dòng là một metric hoặc một point.
- Metric: định nghĩa cấu hình của metrics, ví dụ: tên metric, loại metric,… Một metric có thể là built-in metrics(https://k6.io/docs/using-k6/metrics/#built-in-metrics) hoặc là metric bổ sung định nghĩa bởi người dùng.
- Point: sử dụng đo lường cho metric, một point bao gồm giá trị của một metric tại một thời điểm.
Trong phân loại end-of-test, ngoài định dạng CSV và JSON, chúng ta có thể tùy chỉnh định dạng lưu trữ theo ý muốn cho kết quả bài test thông qua chức năng Custom Summary(https://grafana.com/docs/k6/latest/results-output/end-of-test/custom-summary/).
Tổng kết
Yếu tố hình thành nên một bài kiểm thử thành công không thể thiếu phần trình bày kết quả. Kết quả có tính trực quan, có cấu trúc sẽ giúp kiểm thử viên dễ dàng hơn trong việc phân tích và đưa ra kết luận, báo cáo về hiệu năng của hệ thống.
Hi vọng, thông qua bài viết này, chúng ta sẵn sàng đọc bất kỳ kết quả của bài kiểm thử bằng công cụ K6, tiếp sau đó là hiểu và phân tích, xử lý các dữ liệu này. Và tùy thuộc vào tình huống, chúng ta có thể lưu trữ kết quả ở các dạng khác nhau cho các mục đích khác nhau.
Trong bài viết, chúng ta đã va chạm với định dạng output realtime về mặt khái niệm. Ở bài viết tiếp theo, sẽ hấp dẫn hơn khi chúng ta tiếp tục tìm hiểu thêm về định dạng kết quả của K6 và trực quan hóa chúng thông qua các nội dung thực hành.
Bình luận