最近、シナリオベースの負荷試験ツールを開発している。
ずっと作りたかったのだが、作っても売れる気がしなかったので手を出していなかった。
ただ、結局自分が欲しい負荷試験ツールは自分で作るしか無いと思い一念発起して作ることにした。
売れるかどうかは無視することにした。とりあえずパッケージとして提供するだけしてみようと思う。
Erlang で作る
Tsung という前例があるのと、Erlang/OTP + Lua の経験もあるので。性能はサーバの台数でカバーというので許してほしい。分散しやすい仕組みは作ろうと思う。分散得意らしいし、 Erlang 。
Lua でシナリオが書ける
今回の負荷試験ツールは HTTP/1.1 を利用した仕組みをまずは実装することにした。WebSocket や HTTP/2 対応とかもそのうちやっていきたいが、まずはできるところから。
とりあえずシナリオを Lua で書けるようにしたい。さらに色々な便利ライブラリを提供したいということで、こんなのが動けばいいなというのを考えてみた。
local http = require "shiguredo.http"
local json = require "shiguredo.json"
local base64 = require "shiguredo.base64"
local body = {username = "test", credential = "pass"}
-- python requests を参考に
local res = http.post("https://example.com/login", {}, body)
local resp_json = json.decode(res.body)
local body = {secret_key = resp_json.secret_key}
local res = http.post("https://example.com/get_data", body)
local resp_json = json.decode(res.body)
-- base64 なバイナリを戻す
local data = base64.decode(resp_json.base64ed_data)
HTTPな API 叩いて戻り値をデコードして、中の値を使って再度リクエストを送るといった仕組みを実現できるようにしてみた。
バイナリーの場合は base64 使いそうなのでライブラリとして提供する。
ちなみに、この Lua のコードは Erlang VM の上で動く。そしてこの Lua コードが動くところまではできている。
リクエスト数ではなくユーザ数
リクエスト数より仮想ユーザ数で負荷をかける仕組みを実現することにした。例えば 100 万ユーザが色々なシナリオをで動く仕組みを提供したい。
シナリオとユーザデータを用意しておくことで、仮想ユーザごとの振る舞いを変える事ができるようにしたい。
まだ設計が終わっていないが、ユーザデータも Lua 側から気軽に呼べるようにはするつもりだ。
プラグインモデル
Lua の require “shiguredo.*” というのはビルトインのライブラリとして提供予定。
ただし負荷試験は残念ながら色々要件がある。例えば API が JSON でも Form でもない謎なバイナリであったりするかもしれない。
この辺はプラグインモデルで解決しようと考えている。
local client = require "sample.client"
local data = {spam = egg, ham = bacon}
local res = client.send("127.0.0.1", 5000, data)
つまり、ライブラリをプラグイン形式で提供し etc/plugins/ に sample_client.ez ファイルを置くことで Lua から使えるようにするというイメージ。
ちなみに、 *.ez というのは Erlang アプリケーションを zip で圧縮した形式。公式。
プラグインはまずはクローズドで開発、ただ将来的には仕様をオープンにして気軽に開発できるようにしていきたい。
利用の流れ
まずはシナリオを Lua でいくつか書く。そしたら仮想ユーザ数を決める。仮想ユーザ数分のユーザ初期データを用意する。
あとは終了するまでシナリオを繰り返し実行するというシンプルな仕組みをまず実現しようと開発を進めている。
統計とか測定とか
サーバ側に Newrelic を利用してもらう。当面はこちらでは提供しない。
その他もろもろ
- 1 ヶ月負荷をかけ続けるとかにも対応したい
- シナリオの割合とかを決められるようにしたい、ユーザの 20% はこのシナリオで、とか
- 構築や利用はとにかく簡単にしたい
- WebSocket と HTTP/2 への対応
- 将来的には QUIC や gRPC にも対応したい
- 最初に呼ばれる setup、 最後に呼ばれる clean といった関数または lua ファイルを用意できるようにしたい
-- こんな感じで呼べたりとか
local quic = require "shiguredo.quic"
-- ユーザデータを呼べたりとか
-- openresty を参考
local shared = require "shiguredo.shared"
local row = shared.get_user(100)
local username = row.username
local password = row.password
まとめ
- シナリオベースで気軽に使えて、大量の仮想ユーザでの試験ができる仕組みを提供したい
- シナリオを簡単に書ける仕組みを提供したい
- 継続的に負荷試験が走るのをあたりまえにしたい
- シナリオベースの負荷試験はやってあたりまえにしたい
「負荷試験ツールはじめました」
興味ある方は Twitter で voluntas まで DM いただければ。