iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Rust

Rust 後端入門系列 第 29

Day 29 使用 k6 壓力測試專案

  • 分享至 

  • xImage
  •  

什麼是 k6

  • k6 是一個用於效能測試(負載測試、壓力測試、耐力測試)的開源工具,主要以 JavaScript 撰寫測試腳本。
  • 設計目標是可程式化、可自動化、且容易整合 CI/CD。也有商業雲端服務 k6 Cloud 可用於分散式大流量測試與更豐富的分析。

k6 的優點

  • 腳本採用 ES6+ JavaScript(簡單上手)。
  • 輕量、效能高,適合在本地或 CI 中執行。
  • 支援大量輸出與整合(InfluxDB、Prometheus、Datadog、k6 Cloud)。
  • 內建多種測試型態(stages、vus、duration、thresholds)。

核心概念

  • VU(Virtual User):虛擬使用者數量,模擬同時線上使用者。
  • Iteration:單一 VU 執行一次 default 函式的行為循環次數。
  • stages:用來定義 ramp-up、steady-state、ramp-down 的階段化負載。
  • Thresholds:設定通過/失敗標準(例如 p95 < 500ms、錯誤率 < 1%),超出即視為測試失敗。
  • Checks:對回應做斷言(status code、body 內容等),用來計算成功率。
  • Metrics:k6 自帶多種指標(http_req_duration、http_reqs、checks、vus、iterations),也能自定義 Metric(Trend、Counter、Gauge)。

安裝

請前往官方網站查看詳細的安裝指南

https://grafana.com/docs/k6/latest/set-up/install-k6/

此處簡單說明安裝方式。

MacOS

  • brew install k6

Docker

  • docker pull grafana/k6

Windows

  • choco install k6
  • 或者下載 msi 檔案安裝

測試目標與場景

  • 針對常見 API:/users (POST 建立用戶)、/users/login、/myid。

  • 設計場景:

    模擬登入(POST /users/login)後呼叫受保護資源 /myid

範例 k6 腳本

下面是一個範例 k6 檔案:test.js

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend } from 'k6/metrics';

export let latency = new Trend('latency_ms');

export let options = {
  stages: [
    { duration: '10s', target: 5 },   // 漸增到 5 VUs
    { duration: '30s', target: 5 },    // 維持 5 VUs
    { duration: '10s', target: 0 },    // 使用者數量漸減
  ],
  thresholds: {
    // 95% 的請求應小於 3s
    'http_req_duration': ['p(95)<3000'],
  },
};

const BASE = __ENV.BASE_URL || 'http://localhost:3000';

function randomUserPayload() {
  const id = Math.floor(Math.random() * 1000000);
  return JSON.stringify({
    username: `user_${id}`,
    email: `user_${id}@example.com`,
    password: 'Password123!'
  });
}

export default function () {
  // 模擬混合行為:先建立使用者,然後登入、然後存取 /myid
  let createRes = http.post(`${BASE}/users`, randomUserPayload(), {
    headers: { 'Content-Type': 'application/json' },
  });

  check(createRes, {
    'create user status 201 or 200': (r) => r.status === 201 || r.status === 200,
  });

  let body;
  try {
    body = createRes.json();
  } catch (e) {
    body = {};
  }
  let loginPayload = JSON.stringify({
    username: body.username || 'user_1',
    password: 'Password123!'
  });

  let loginRes = http.post(`${BASE}/users/login`, loginPayload, {
    headers: { 'Content-Type': 'application/json' },
  });

  const token = (loginRes.status === 200 && loginRes.json().token) ? loginRes.json().token : null;

  if (token) {
    let myidRes = http.get(`${BASE}/myid`, { headers: { Authorization: `Bearer ${token}` }});
    check(myidRes, {
      'myid 200': (r) => r.status === 200,
    });
  }

  latency.add(createRes.timings.duration);
  sleep(Math.random() * 2); // 模擬使用者思考的時間
}

說明:

  • 這支腳本會建立使用者、登入再呼叫 /myid。

執行 k6 測試

使用預設的 stages:

k6 run test.js

也可以自訂設定,如同時 50 個 VU 持續跑 1 分鐘:

k6 run --vus 50 --duration 1m test.js

觀察結果與解讀

k6 執行完會輸出:

  • http_req_duration(平均、p(90)、p(95)):延遲的指標,主要看 p95。
  • iterations / vus / vus_max:併發量與實際 VU 數。
  • 若 thresholds 未達成條件會顯示失敗。

測試結果如下

C:\Users\gen\rust\sqlx_connect_demo>k6 run test.js

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

     execution: local
        script: test.js
        output: -

     scenarios: (100.00%) 1 scenario, 5 max VUs, 1m20s max duration (incl. graceful stop):
              * default: Up to 5 looping VUs for 50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)

  █ THRESHOLDS

    http_req_duration
    ✓ 'p(95)<3000' p(95)=2.89s

  █ TOTAL RESULTS

    checks_total.......: 55      1.049661/s
    checks_succeeded...: 100.00% 55 out of 55
    checks_failed......: 0.00%   0 out of 55

    ✓ create user status 201 or 200

    CUSTOM
    latency_ms.....................: avg=2760.95882 min=2588.8425 med=2735.2139 max=2965.1841 p(90)=2891.3154 p(95)=2942.31383

    HTTP
    http_req_duration..............: avg=1.38s      min=784.5µs   med=1.29s     max=2.96s     p(90)=2.84s     p(95)=2.89s
      { expected_response:true }...: avg=2.76s      min=2.58s     med=2.73s     max=2.96s     p(90)=2.89s     p(95)=2.94s
    http_req_failed................: 50.00% 55 out of 110
    http_reqs......................: 110    2.099323/s

    EXECUTION
    iteration_duration.............: avg=3.85s      min=2.71s     med=3.82s     max=4.77s     p(90)=4.52s     p(95)=4.69s
    iterations.....................: 55     1.049661/s
    vus............................: 1      min=1         max=5
    vus_max........................: 5      min=5         max=5

    NETWORK
    data_received..................: 43 kB  820 B/s
    data_sent......................: 22 kB  419 B/s

running (0m52.4s), 0/5 VUs, 55 complete and 0 interrupted iterations
default ✓ [======================================] 0/5 VUs  50s

常見測試策略與建議

  • 先做 smoke test:1-5 VU、短 duration,確認功能正確。
  • 做基準測試(baseline):穩定小負載,量測 p95。
  • 做壓力測試(stress):逐漸增加 load,找到崩潰點。
  • 做耐力測試(soak):長時間維持中高負載,觀察資源洩漏或累積問題。
  • 用 realistic data patterns:隨機化、think time、混合讀寫。

上一篇
Day 28 Axum 整合 GitHub Actions 完成 CI
下一篇
Day30 學習總結
系列文
Rust 後端入門30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言