高パフォーマンスなスタブ(モック)サーバーを作った
負荷試験にも耐えうる高パフォーマンスなスタブ(モック)サーバーを作成しました。
返したいレスポンスを含んだ設定ファイルを指定するだけで、お好みのレスポンスを返却します。
背景
以前、チーム内でスタブ(モック)を共有できるGUIツールを作りました。
ただ実装がNode.jsということもあり、パフォーマンスが出せず、
負荷試験には適さないという課題があったため、それを補うべく高パフォーマンスなスタブサーバーを作ることにしました。
技術選定
C, C++, Rust, Goなどがパフォーマンスを出しやすいだろうなと思ってましたが、
楽なほうに流されるエンジニアとしての自負がありましたので、Nim言語を採用しました。
Nim言語の特徴として、トランスパイラであり、Cに最適化した上でトランスパイルしてくれるため、
パフォーマンスとしては実質Cというところに惹かれました。
また、書き味もスクリプト言語を触っていれば違和感なく触れるため、学習コストも低いです。
アプリケーション本体にNim言語を使うのはマイナーなため怖いですが、簡単なツールという立ち位置であれば十分使えると判断しました。
使い方
冒頭でも述べた通り、broly
に設定ファイルを指定するだけで利用可能です。
(ポート指定が必要であれば、--portまたは-pオプションで指定ください。)
brory hogehoge.json
設定ファイル(hogehoge.json)の中身
[
{
"path": "/hello",
"method": "GET",
"contentType": "application/json",
"statusCode": "200",
"response": "{\"name\": \"hello\"}",
"sleep": 0
},
{
"path": "/world",
"method": "GET",
"contentType": "application/json",
"statusCode": "200",
"response": "{\"name\": \"world\"}",
"sleep": 0
}
]
実際にリクエストしてみると、ちゃんと設定されたレスポンスが返却されてます。
curl -v localhost:8080/hello
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Length: 17
< Server: HttpBeast
< Date: Wed, 27 Jul 2022 15:04:40 GMT
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"name": "hello"}%
パフォーマンス
では、実際にどの程度のパフォーマンスなのかを
実際に負荷試験で利用されていそうなWireMockというスタブサーバーと比較してみます。
検証ツール
検証にあたって、負荷試験ツールとしてVegetaを採用します。
検証用コマンド
echo 'GET http://localhost:9999/hello' | vegeta attack -duration=60s | vegeta report
検証用の環境
検証環境としては、ローカルのDockerContainer上で、
それぞれBrolyとWireMockを実行します。
- Broly
docker run --init \
-p $HOST_PORT:8080 \
--mount type=bind,src=hogehoge.json,dst=/target.json \
ghcr.io/solaoi/broly:latest
- WireMock
docker run --init -d -p 8888:8080 -v $(pwd)/stubs:/home/wiremock wiremock/wiremock:2.33.2
検証結果
レイテンシーに差が結構でていますね。
Brolyのほうが99%タイルでおよそ10倍の性能を出す結果となりました。
- Broly
Requests [total, rate, throughput] 3000, 50.02, 50.01
Duration [total, attack, wait] 59.985s, 59.982s, 3.197ms
Latencies [min, mean, 50, 90, 95, 99, max] 958.602µs, 2.906ms, 2.989ms, 3.334ms, 3.439ms, 3.682ms, 8.954ms
Bytes In [total, mean] 51000, 17.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:3000
Error Set:
- WireMock
equests [total, rate, throughput] 3000, 50.02, 50.02
Duration [total, attack, wait] 59.98s, 59.976s, 3.466ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.036ms, 6.185ms, 3.421ms, 4.056ms, 4.36ms, 34.511ms, 567.831ms
Bytes In [total, mean] 48000, 16.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:3000
Error Set:
インストール方法
負荷試験用にサーバーを配置すると思いますので、バイナリやDockerImageが用意してあります。
またシンプルに動作確認されたい場合は、brewで簡単にインストール可能です。
brew install solaoi/tap/broly
親和性
co-metubユーザーは、GUIで作ったスタブをそのままBroly用のJSONとしてエクスポートできます。
開発初期にスタブとして用意していたものをそのまま負荷試験でも転用できて、とても楽ちんですね。
まとめ
初めてNim言語を触ってみましたが、この書き味でこの性能が出ることに感動しました。
GoやDenoでツールを作ってみたことがあるのですが、今後ツールを作るならNimで!と思わせてくれる素晴らしい言語だと思います。
また、今回のBrolyのロゴは嫁にリクエストして描いてもらいました。
無茶なリクエストを聞いていただき、この場で御礼申し上げます。
最後に、co-metubも社内で揉まれ、大分こなれてきましたので、良ければこちらもお試しくださいませ。
※ PrismaでSQLiteを使用時に、並列リクエストを受け取るとエラーとなるといった各種バグを踏み抜きました;;
では、またどこかでお会いしましょう!
スターください🙏
Discussion
高パフォーマンスなスタブサーバーの作成にNim言語を選んだ背景や、実際のパフォーマンス比較も非常に参考になります。負荷試験ツールVegetaを使った比較結果でBrolyがWireMockより圧倒的に優れている点は驚きです。