使用 k6 進行壓力測試

當系統預期有很多人同時上線時,可能會需要進行壓力測試。比起老牌工具 Apache Jmeter 的高學習難度,k6 是一套很好的壓力測試工具,對於開發人員來說非常友善。

本文將簡單介紹如何使用 k6 進行壓力測試。

安裝 k6

k6 是使用 GO 語言撰寫的,且支援各種平台,包括 Windows、Linux 和 Mac。

安裝 k6 的方式非常簡單。如果你使用 Windows 並已安裝 Chocolatey,只要執行下列指令即可安裝 k6:

choco install k6

此外,k6 官方也提供 docker image。只要拉取 docker image,即可使用包含 k6 的環境,而不用擔心會污染現有的環境。

docker pull grafana/k6

我們也可以前往 k6 的 GitHub Release 頁面,直接下載 binary 執行檔來使用。

其他平台的使用方式可以參考官方文件:

https://k6.io/docs/get-started/installation/

開始撰寫 k6 腳本

k6 的核心雖然是用 GO 語言寫的,但它的腳本語法使用的是 JavaScript,以下是個簡單的 k6 壓測腳本:

// script.js
import http from 'k6/http';
import { sleep } from 'k6';

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

程式碼應該是非常好理解的,我們使用 k6/http 的 http 物件,透過其 get 方法,去呼叫 https://test.k6.io 這個網址,並且在呼叫完後,使用 k6sleep 方法,讓程式暫停 1 秒,這樣就完成了一個簡單的壓力測試。

接著我們就可以執行以下指令來執行這個腳本:

k6 run script.js

就完成一次對目標網址的測試啦!接著就可以看以下的執行結果:

從圖中可以看到 k6 呼叫的狀況,包含了每秒呼叫的次數、每次呼叫的時間、每次呼叫的狀態碼等等,這些資訊都可以幫助我們了解目標網站的回應能力的狀況。

壓力測試

如果只是呼叫一次網址,那其實沒什麼壓力,如果希望能測試伺服器對大量情求的處理能力,可以透過 k6 增加更多虛擬的使用者來進行測試,最簡單的方式是執行 k6 時加上 --vus 參數,指定要模擬多少使用者

k6 run --vus 10 scripts.js

如果想要這些使用者持續不斷的呼叫網址,可以加上 --duration 參數,指定要持續多久,例如:

k6 run --vus 10 --duration 30s scripts.js

這麼一來就可以模擬同時 10 個使用者,在 30 秒內持續呼叫得到的結果,如果都能在合理的時間內回應,那就代表伺服器的處理能力是足夠的。

瞬間大量使用者是一種情境,另外還有一種可能是使用者在一段時間內持續增加,這時候我們可以在程式內設定增加的方式,例如:

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

export const options = {
  stages: [
    { duration: '30s', target: 20 },
    { duration: '1m30s', target: 10 },
    { duration: '20s', target: 0 },
  ]
};

export default function () {
  const res = http.get('https://httpbin.test.k6.io/');
  check(res, { 'status was 200': (r) => r.status == 200 });
  sleep(1);
}

只要些個 export const options = {},這個物件就會告訴 k6 要以什麼樣的方式進行模擬,以上面的例子來說,我們分成了三個階段,剛開始會再 30 秒內,增加到 20 個使用者同時上線;接著 1 分 30 秒內,維持 10 個使用者;最後 20 秒內,再將使用者降為 0。

測試回應結果

除了一般呼叫網站看結果之外,我們也可以透過 k6 來測試回應的內容,例如:

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

export default function () {
  const loginBody = JSON.stringify({
    username: 'mike',
    password: '123456'
  });

  const loginResponse = http.post(
    `http://someapi/login`,
    loginBody,
    {
      headers: {
        "Content-Type": "application/json",
      },
    }
  );
  check(loginResponse, {
    "登入: is status 200": (r) => r.status === 200,
    "登入: has access token": (r) => !!r.json().accessToken,
  });
}

首先我們先用 http.post 去呼叫 API 並取得回傳結果,接著用 check 來檢查回傳的結果,這邊我們檢查了回傳的狀態碼是否為 200,以及回傳的內容是否有 accessToken,如果都符合的話,就會回傳 true,反之則是 false。

透過 check 來檢查,在執行測試後會出現如下的結果:

測試門檻

我們也可以為回傳結果的成功率、回應時間等等,設定門檻,如果超過門檻的話,就會將測試結果標示為失敗,例如:

export const options = {
  stages: [
    { duration: "10s", target: 5 },
    { duration: "5s", target: 0 },
  ],
  thresholds: {
    // 期望在整個測試執行過程中,錯誤率必須低於 5%
    http_req_failed: ["rate<0.05"],
    // 平均請求必須在 300ms 內完成,90% 的請求必須在 200ms 內完成
    http_req_duration: ["avg < 300", "p(90) < 100"],
  }
};

thresholds: {} 中,我可以指定評量項目的門檻值,在測試完成後會再這些項目上標記成功或失敗:

http_req_failed 的比率小於設定的 0.05,達到我們設定的門檻內,因此標記為成功; http_req_durationp(90) 時間為 135.66ms,未達我們設定的門檻,因此標記為失敗。

本日小結

k6 可以說是對開發人員最友好的一套負載測試工具,沒有複雜的 UI 操作,使用的都是我們熟悉的程式碼,而且透過程式碼更容易控制測試的情境,更精準的測試出目標網站的狀況。

在 k6 的文件內還有更多的情境,可以在文件上慢慢挖寶!

https://k6.io/docs/

如果您覺得我的文章有幫助,歡迎免費成為 LikeCoin 會員,幫我的文章拍手 5 次表示支持!