🔫

即席負荷試験ツールを実装するためのライブラリを作りました

2022/09/22に公開

モチベーション

世の中には様々な負荷試験ツールがあり、それらは大概シナリオを組めたり複数ノードからリクエストを送れたりと高機能です。無論それは素晴らしいことなのですが

  • 新しく実装したAPIで目標RPSを達しているかざっくり確認したい
  • インデックスの追加前後でパフォーマンスに大きな影響が出ないか見てみたい

等といったシンプルな要件においてはセットアップ諸々の手間を考慮すると少々億劫な面があると思います。かといって自分で即席ツールを実装しようとすると

  • 試験パラメーター取得処理
  • パフォーマンスを意識した実装
  • 並行処理の排他制御
  • 統計情報の出力

等々地味に面倒な実装が多くこちらもそれなりの労力を要します。或いはもっと簡便な試験ツールを使おうとすると微妙に痒いところに手が届かなかったりもするでしょう。

こうしたケースで簡単に負荷試験ツールを実装できるライブラリがあればもっと気軽に負荷試験を行え便利なのではと思い Otchkiss を作成しました。

https://github.com/ryo-yamaoka/otchkiss

読みは「オチキス」で由来はGatling(多銃身回転式機関銃)ほどの火力じゃないよなぁ → そういえば機関銃といえばHotchkissという思いつきからモジった名前にしました。
頭にHが無いのはタイポではなくフランス読み+何か商標的にまずいかもしれないので念の為違う文字列にしておくという意図です。

使用感

$ go run ./sample/...

[Setting]
* warm up time:   5s
* duration:       1s
* max concurrent: 1

[Request]
* total:      90
* succeeded:  90
* failed:     0
* error rate: 0 %
* RPS:        90

[Latency]
* max: 11.0 ms
* min: 10.0 ms
* avg: 10.9 ms
* med: 11.0 ms
* 99th percentile: 11.0 ms
* 90th percentile: 11.0 ms

使い方

基本

ちょっと使ってみるのであればサンプルを見てみるとなんとなくわかるかと思いますが、以下のステップが基本的な流れになります。

  1. Requester interfaceを実装する
  2. interfaceの実装をOtchkissに渡しStart()を呼ぶ
  3. Report()を呼び結果を出力する
  4. 上記コードをビルドしCLIツールにする

https://github.com/ryo-yamaoka/otchkiss/blob/main/sample/main.go

ちなみに otchkiss.New() を使って実装した場合以下のコマンドラインオプションで実行時に設定値を変更することができるためお試し→本試験のような場合でも再ビルドする必要はありません。

  • -p: ここで指定した数だけgoroutineを起動し並行的にリクエストを行う。
  • -d: テストの実行時間。
  • -w: リクエスト先をウォームアップする時間。ここで指定した時間の間リクエストは飛ぶがテスト結果に反映されない。

-d, -wがわかりづらいですが、実際に試験対象にリクエストが飛び続ける時間は d + w, テスト結果として集計対象となるのが d で指定した時間となります。

並行数・実行時間を指定する

前述の通り基本的な実装ではコマンドラインで指定した値もしくはデフォルトの値を使用しますがこの値を固定したい場合はハードコードすることも可能です。

その場合はサブパッケージである github.com/ryo-yamaoka/otchkiss/setting から設定structへ値を入れて otchkiss.FromConfig() に渡して下さい。

	s := &setting.Setting{
		MaxConcurrent: 10,
		RunDuration: 60 * time.Second,
		WarmUpTime: 10 * time.Second,
	}
	ot, err = otchkiss.FromConfig(&SampleRequester{}, s, 1000)

パフォーマンスチューニング

Otchkissは多数の平行リクエストを送りその成否・レイテンシーを記録していますが、内部的にはスライスが使われています。

方々のブログ記事で書かれているためご存知の方も多いかもしれませんがGoのスライスはアロケーションされたメモリが不足すると更に大きいメモリ領域を確保する、という振る舞いをします(参考: Go Slices: usage and internals)。

このため頻繁なアロケーションが発生するとパフォーマンス(=RPS)に影響を及ぼす可能性があります。かといってメモリは有限のためあまり極端に大きくアロケーションすることもよろしくありません。

デフォルトでは100,000,000リクエスト分アロケーションするようになっていますが、最適値は各々異なると思うのでこれも指定できるようにしてあります。先程のサンプルコードにシレッと登場していますが下記コードの最終引数がそれなので必要に応じて指定して下さい(ちなみに↓のように10億を指定すると起動した瞬間にメモリが数GB食われます)。

	ot, err = otchkiss.FromConfig(&SampleRequester{}, s, 1000*1000*1000)

結果の表示フォーマットを変更する

デフォルトの表示は冒頭に示した通りですが otchkiss.TemplateReport() を使うことでお好みのフォーマットで出力できます。指定方法はGoのtemplateそのものを渡すだけなので特段難しいことはないでしょう。

パラメーターは下記デフォルトテンプレートに使われているものが全てです。

https://github.com/ryo-yamaoka/otchkiss/blob/main/template.go

TODO

  • ヒストグラムの表示
  • 時系列のグラフ等ビジュアル面の強化

Discussion