iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

身分驗證與售票流程開發完畢後就可以先來做個簡單的壓力測試了,進行壓力測試當然要有合適的工具來進行,壓力測試的工具有非常多種,如 Jmeter、Locust、K6 等等,其中歷史最悠久最多人使用的應該就是 Jmeter ,但 Jmeter 對硬體效能的要求較高,我的個人電腦可能壓不到系統的上限,Locust 跟 K6 都是相對較新架構的壓測工具,使用起來差異也不大,個人較熟悉 K6,今天就選用 K6 來進行壓力測試。

K6 介紹

以下我們一樣有請 Gemini 來跟我們簡單的介紹一下 K6

K6 是一款功能強大且易於使用的負載測試工具,專門用於測試軟體系統在高流量或高負載情況下的性能表現。它採用 JavaScript 作為腳本語言,讓開發人員能夠輕鬆地撰寫複雜的測試。

K6 的主要優勢:

  • JavaScript-based: 使用熟悉的 JavaScript 語言,降低學習門檻。
  • 高性能: 基於 Go 語言開發,執行效率高。
  • 可擴展性: 提供豐富的 API 和插件,可輕鬆定制測試。
  • 雲端整合: 可以與雲端服務 (如 k6 Cloud) 整合,提供更深入的分析和報告。
  • 開源免費: 社群活躍,資源豐富。

安裝

K6 是開源的專案,依照自己的 OS 直接到 Github 上抓取最新 Release 的版本

Releases · grafana/k6 (github.com)

安裝好後在 Terminal 輸入 K6 就可以看到相關的參數了

https://ithelp.ithome.com.tw/upload/images/20240928/20168312OuN8XWkRPd.png

腳本撰寫

K6 是使用 Javascript 來撰寫腳本的,在任意一個位置建立一個 .js 檔案

import http from 'k6/http';
export const options = {
  vus: 1,
  iterations: 1
};
export default function () {
  http.get('https://[SERVICE_URL]/PubSubTest');
}

測試執行看看

k6 run script.js

https://ithelp.ithome.com.tw/upload/images/20240928/201683129nKD8miA5B.png
可以看到成功送出了 1 個 Request ,並且有詳細的時間資訊。

簡單說明一下腳本的內容,最上方的 options 是要給 k6 的參數,詳細的參數可以使用 help 來查看有哪些

k6 run help

可以在 script 最上方指定,也可以在執行腳本的 command 後在加入參數,要注意的是 command 所下的參數是會覆蓋掉 script 內部的參數的。

以下列出一些常用的參數及說明

  • vus: 使用者數量,代表要模擬多少個使用者同時執行script
  • duration: 測試的最長時間
  • iterations: 總執行次數
  • rps: 限制每秒的 request 數量
  • user-agent: 設定 Header 裡面的 Agent 預設是 “k6/0.52.0 (https://k6.io/)”

這邊只會用到前三個參數,要測試之前要準備好很多的帳號跟一個有很多座位的活動,當然這些可以透過人工建立或是透過其他工具來處理,也可以直接透過 k6 來完成這些要求。

建立帳號 Script

我們這裡就用一個 for 迴圈慢慢去建立 100 個帳號

import http from 'k6/http';
export const options = {
    vus: 1,
    iterations: 1
};
const baseUrl = 'https://[SERVICE_URL]';

const arrayLength = 1000;
const resultArray = [];

for (let i = 0; i < arrayLength; i++) {
    resultArray.push({
        username: 'Test' + i,
        password: 'Test' + i
    });
}

export default function () {
    const vuId = __VU;
    for (let i = 0; i < arrayLength; i++) {
        http.post(baseUrl + '/api/auth/user', JSON.stringify(
            resultArray[i]
        ), {
            headers: {
                'Content-Type': 'application/json',
            },
        });
    }
}

檢查是否有建立成功

https://ithelp.ithome.com.tw/upload/images/20240928/20168312c34IXkHNjF.png

建立活動 Script

這裡一樣用 1 個 vus 執行一次,建立一個有 60000 個位子的活動

import http from 'k6/http';
export const options = {
    vus: 1,
    duration: '30s',
    iterations: 1
};
const baseUrl = 'https://ithome2024-salesservice-539812124803.asia-east1.run.app';

const arrayLength = 60000;
const resultArray = [];

for (let i = 0; i < arrayLength; i++) {
    resultArray.push({
        name: i.toString(),
    });
}

export default function () {
    var res = http.post(baseUrl + '/api/event', JSON.stringify({
        "name": "TestEvent001",
        "eventDate": "2024-09-25T15:15:39",
        "startSalesDate": "2024-09-25T15:15:39",
        "endSalesDate": "2024-09-25T15:15:39",
        "description": "Desc",
        "remark": "Re",
        "seats": resultArray
    }), {
        headers: {
            'Content-Type': 'application/json',
        },
    });
    console.log(res.status);

}

檢查是否有建立成功

https://ithelp.ithome.com.tw/upload/images/20240928/20168312Y9jw4JoKNs.png
OK 這樣壓力測試的事前資料就準備好了

購票 Script

購票的 script 會稍微複雜些,因為我們每個 vus 都需要登入,登入後取得 token 再進行購票,而購票前也要先取得座位的資訊才能購票,可以看到這裡我們新增了一個 setup 的 function 這個 function 是開始壓測前會先執行的 script 可以在裡面準備好一些要給 vus 重複使用的資訊。

import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
    vus: 100,
    duration: '30s',
};
const baseUrl = 'https://ithome2024-salesservice-539812124803.asia-east1.run.app';
const eventId = 21;

const vuData = {
    tokens: [],
    seats: []
};

export function setup() {
    var res = http.get(baseUrl + '/api/event/' + eventId);
    var event = JSON.parse(res.body);
    vuData.seats = event.seats;
    for (let i = 0; i < options.vus; i++) {
        let res = http.post(baseUrl + '/api/auth', JSON.stringify({
            username: 'Test' + i,
            password: 'Test' + i
        }), {
            headers: {
                'Content-Type': 'application/json',
            },
        });
        vuData.tokens.push(JSON.parse(res.body).token);
    }
    return vuData
}

export default function (data) {
    const randomIndex = Math.floor(Math.random() * data.seats.length);

    // const randomSeat = data.seats[randomIndex];
    let body = {
        "eventId": eventId,
        "seatId": data.seats[randomIndex].id,
        "createTime": "2024-09-25T15:15:39"
    };
    let res = http.post(baseUrl + '/api/ticket', JSON.stringify(body), {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + data.tokens[__VU - 1]
        },
    });
    if (res.status !== 201) {
        console.log(res.status);
        console.log('request Body: ' + JSON.stringify(body));
        console.log('response body: ' + res.body);
    }
}

壓完之後可以看到 RPS 慘不忍睹只有 27,min 得 http wating 只有 77 毫秒,但是 p95 有道8秒非常之長,平均也要 1.8秒左右,很明顯搶票 API 目前效能是很差的 100vus 都無法應付。

https://ithelp.ithome.com.tw/upload/images/20240928/2016831294WVVhMdku.png
購票的 API 只有跟 Redis 有IO互動以及最後推送購票資訊給 Pub/Sub,理論上 Redis 跟 Pus/Sub 都是是能夠承受到 百萬級別的 QPS 的,後續要再針對購票的 API 來做調整,找出問題在哪裡並持續的優化它。


上一篇
Day26: 實作-開發-Process
下一篇
Day28: 實作-效能調校
系列文
窮小子的售票系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言