k6/metricsを用いて出力値を操作する

## 概要

k6には任意の値を集計することでカスタマイズした出力値を得ることができる機能がある。これを利用してk6でイマイチ充実していないWebSocketの負荷試験の結果をカスタマイズしてみる。

## 4種類のMetricsタイプ

以下の四種がある。

* Counters 
    * 回数を数える時に使用する
* Gauges 
    * 追加した複数の値から「最大値」「最小値」「最新の値」のみを取得できる
* Rates 
    * 追加された「1」(正確にはnon-zero)か「0」の値の割合を取得できる
* Trends 
    * 追加された複数の値から「最大値」「最小値」「平均」「パーセンタイル」等の統計情報を取得できる。

それぞれ以下のように使用する。

```js
const counter = new Counter("countTest");
const gauge  = new Gauge("gaugeTest");
const rate = new Rate("rateTest");
const trend = new Trend("trendTest");


export default function(){

    // (略) nを1から193まで増やしたとする

    counter.add(1);
    gauge.add(n);
    rate.add(n%2);
    trend.add(n);
}
```

こうすることで、出力値に例えば以下のような値が含まれるようになる。

```
countTest.............: 193    189.327389/s
gaugeTest.............: 193    min=1        max=193
rateTest..............: 50.25% ✓ 97         ✗ 96   
trendTest.............: avg=97     min=1       med=97     max=193     p(90)=173.8  p(95)=183.4
```


## websocket通信にかかる時間を計測する

k6が標準で持つ機能では、websocket通信の計測は「通信を開始してから切断するまで」の一区切りしか取得することはできない。
metricsのTrendを用いることで、一回分のwebsocket接続中に、リクエスト送信からレスポンス受け取りまでの1試行ごとの応答時間を計測してみる。

まず簡単なサーバーを用意する。
```js
import http from 'http';
import websocket from 'ws'

const server: http.Server = http.createServer();
const wss = new websocket.Server({server: server})

wss.on('connection', ws => {
    ws.on('message', message => {
      setTimeout(()=>{ws.send(message.toString())}, 100);
    })
  })

const port = 3000;
server.listen(port, () => console.log('app listening on port ' + port));
```
100ms待ってから送られてきたものをそのまま返すだけ。

負荷試験用のスクリプトは以下のようなものを作成した。

```js
import ws from 'k6/ws';
import {Trend } from 'k6/metrics';

const trend = new Trend("trendTest");


export const options = {
  duration: '5s',
}

let count = 0;
let requestTime = new Map();

export default function () {
  const url = 'ws://localhost:3000';
  const res = ws.connect(url, null, function (socket) {
    socket.on('open', function () {
      console.log('connected');
      socket.setInterval(() => {
        socket.send(JSON.stringify({id: count}));
        requestTime.set(count, Date.now());
        count++;
      }, 100);
    });
    socket.on("message", (data) => {
      const message = JSON.parse(data);
      const startTime = requestTime.get(message.id);
      const endTime = Date.now() - startTime;
      trend.add(endTime);


    })
    socket.setTimeout(() => {
      socket.close();
    }, 5000);
  });
}
```

100msに一回メッセージを送信し、その際の時刻をidと共に記録しておく。

レスポンスからidを取得し、対応するリクエストの時刻と現在の時刻からかかった時間を計測する。

以下のような出力が得られた。

```
     data_received.........: 647 B 129 B/s
     data_sent.............: 941 B 188 B/s
     iteration_duration....: avg=5s         min=5s     med=5s     max=5s     p(90)=5s     p(95)=5s    
     iterations............: 1     0.199671/s
     trendTest.............: avg=103.104167 min=100    med=101    max=115    p(90)=109    p(95)=112.3 
     vus...................: 1     min=1      max=1
     vus_max...............: 1     min=1      max=1
     ws_connecting.........: avg=5.06ms     min=5.06ms med=5.06ms max=5.06ms p(90)=5.06ms p(95)=5.06ms
     ws_msgs_received......: 48    9.584225/s
     ws_msgs_sent..........: 50    9.983567/s
     ws_session_duration...: avg=5s         min=5s     med=5s     max=5s     p(90)=5s     p(95)=5s    
     ws_sessions...........: 1     0.199671/s
```

trendTestとして、リクエストの送信からレスポンスの受け取りまでに要する時間が統計値として取得できた。