k6って何?
オープンソースの負荷試験ツール。http 接続のみでなく、grpc や websocket にも対応している。
今回はこれを用いて WebSocket の負荷試験を行う。
インストール
公式サイト参照。ubuntuであれば以下のコマンドを打ち込む。
https://k6.io/docs/getting-started/installation/
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
この通り実行すればヨシ。
途中で
gpg: failed to create temporary file '/root/.gnupg/...(略)' : No such file or director
と言われたので確認したら、/root/.gnupg がなかったので作成した。
sudo mkdir /root/.gnupg
スクリプトの作成
以下の四つのステップによって成り立っている。
// 1. init
export function setup() {
// 2. step3を実行する前準備として一回だけ実行される
// ここの返り値をstep3の引数として渡すことができる
}
export default function (data) {
// 3. 試すテスト
}
export function teardown(data) {
// 4. 全テスト終了時に実行される
}
- init で他のモジュールからインポート等の前準備を行う
- setup でテスト前に必要な準備を行う。これはステップ3を行う前に一度だけ実行される
- default functionとして実行するテストを描く
- 全テスト終了時に実行される。
なおstep3以外は省略可能。default functionのみが実行される。
httpの場合の例
http接続の場合、例えば以下のようなスクリプトとなる。
https://k6.io/docs/using-k6/checks/
import { check } from 'k6';
import http from 'k6/http';
export const options = {
vus: 10,
iterations: 10,
};
export default function () {
const res = http.get('http://test.k6.io/');
check(res, {
'verify homepage text': (r) =>
r.body.includes('Collection of simple web-pages suitable for load testing'),
});
}
- optionsで指定したvus(バーチャルユーザー)が並列の実行数、iterationsが繰り返しの回数。これらの値はコマンド実行時の引数としても指定が可能。
- check()で得られたレスポンスを検証する。
- k6/httpパッケージからhttp.get()を実行する。
以下のようにして実行する。
k6 run script.js
出力値が得られる。
running (00m01.0s), 00/10 VUs, 10 complete and 0 interrupted iterations
default ✓ [================================] 10 VUs 00m01.0s/10m0s 10/10 shared iters
✓ verify homepage text
checks.........................: 100.00% ✓ 10 ✗ 0
data_received..................: 173 kB 170 kB/s
data_sent......................: 5.4 kB 5.3 kB/s
http_req_blocked...............: avg=295.59ms min=189.1ms med=294.41ms max=411.22ms p(90)=409.23ms p(95)=410.19ms
http_req_connecting............: avg=182.76ms min=174.11ms med=184.7ms max=190.56ms p(90)=187.51ms p(95)=189.21ms
http_req_duration..............: avg=203.91ms min=180.19ms med=202.93ms max=227.96ms p(90)=222.8ms p(95)=227ms
{ expected_response:true }...: avg=203.91ms min=180.19ms med=202.93ms max=227.96ms p(90)=222.8ms p(95)=227ms
http_req_failed................: 0.00% ✓ 0 ✗ 20
http_req_receiving.............: avg=1.12ms min=0s med=0s max=16.71ms p(90)=1.15ms p(95)=3.18ms
http_req_sending...............: avg=72.81µs min=0s med=0s max=1.42ms
p(90)=18.2µs p(95)=88.29µs
http_req_tls_handshaking.......: avg=105.13ms min=0s med=99.28ms max=224.41ms p(90)=222.98ms p(95)=223.89ms
http_req_waiting...............: avg=202.71ms min=179.18ms med=202.93ms max=227.96ms p(90)=220ms p(95)=222.1ms
http_reqs......................: 20 19.681399/s
iteration_duration.............: avg=999.27ms min=980.82ms med=1s max=1.01s
p(90)=1.01s p(95)=1.01s
iterations.....................: 10 9.8407/s
vus............................: 7 min=7 max=7
vus_max........................: 10 min=10 max=10
ここまでがhttpでの例。
k6/ws
k6でwebsocketを扱う時は、k6/wsパッケージを用いる。
http接続ではres = http.get('http://test.k6.io/');
としてレスポンスを渡したのに対して、ws接続ではres = ws.connect()
としてwsが接続してから切断するまでの処理を渡す。
import ws from 'k6/ws';
export default function () {
const url = 'ws://echo.websocket.org';
const res = ws.connect(url, null, function (socket) {
socket.on('open', function () {
console.log('WebSocket connection established!');
socket.close();
});
socket.on("message", function (message) {
const msg = JSON.parse(message);
console.log(msg);
});
});
console.log(res);
check(res, { 'Connected successfully': (r) => r && r.status === 101 });
}
なお、この際にres
として得られる値はbody
の値を持たない。
console.log(res);
//{"url": "ws://echo.websocket.org","status":101,(略)"Connection":"upgrade","Upgrade":"WebSocket","Sec-Websocket-Accept":""},"body":"","error":""}
なのでbodyの値で検証といったことはできず、ステータスが101であるかを確認する程度となる。
check(res, { 'Connected successfully': (r) => r && r.status === 101 });
以下でメソッドの解説を行う。
connect
connectは
connect( url, params, callback )
と三つの引数を持つ。websocket接続の際にヘッダーやタグを設定する必要があれば、paramsに引き渡す。
なお、Connection, Upgrade, Sec-WebSocket-Keyといったwebsocket接続にそもそも必要なヘッダーはデフォルトで含まれているので入れる必要がない。
Socket.send(data)
send()でメッセージの送信を行う。
const res = ws.connect(url, null, function (socket) {
socket.on("open", function open() {
const message = { hoge: "fuga" };
socket.send(JSON.stringify(message));
});
}
Socket.setInterval(callback, interval)
setInterval()を使用する際はSocket.setInterval()を使用する。
定期的に送信を行うテストで使用できる。
const res = ws.connect(url, params, function (socket) {
socket.on('open', function open() {
socket.setInterval(function timeout() {
const message = { hoge: "fuga" };
socket.send(JSON.stringify(message));
}, 1000);
});
Socket.setTimeout(callback, period)
setTimeoutを使用する際はSocket.setTimeout
を使用する。
socket.setTimeout(function () {
socket.close();
}, 2000);
Socket.close()
socket.close();
接続を閉じる。これを実行するとws.connect()
の処理が終了し、値が返却される。
実行方法
以下のようにして実行する。
k6 run script.js
出力値が得られる。
data_received........: 4.1 kB 4.7 kB/s
data_sent............: 197 B 226 B/s
iteration_duration...: avg=869.59ms min=869.59ms med=869.59ms max=869.59ms p(90)=869.59ms p(95)=869.59ms
iterations...........: 1 1.148347/s
ws_connecting........: avg=869.14ms min=869.14ms med=869.14ms max=869.14ms p(90)=869.14ms p(95)=869.14ms
ws_sessions..........: 1 1.148347/s
ws_msgs_received......: 1 2/s
ws_msgs_sent..........: 1 2/s
- iteration_durationが実行にかかった時間
- iterationsが繰り返した回数
- ws_connectingがws接続に必要な時間
- ws_msgs_receivedが受信したメッセージの数
- ws_msgs_sentが送信したメッセージの数
なお、handleSummary関数を用いて出力値を変えることもできる。
とりあえず全部出力してみる。
// 終了時に実行される
export function handleSummary(data) {
return data
}