Web API用の負荷テストツールで遊んでみる
これはWeb API Advent Calendar 2014の16日目の記事です。
はじめに
Web API Advent Calendar 2014 8日目ではkazuchikaさんよりApigee製のAPI負荷ツールapibの紹介がありました。
この記事ではapibに加えてBoomというGolang製の類似ツールを簡単に紹介し、様々なマイクロWebフレームワークに対して両者を使ってみます。
Boom
- Python製のAPI負荷テストツールBoomをGo言語に移植したもの
- Googleエンジニアが開発
- Apache Bench(ab)と同じくリクエスト回数を指定した計測
- レスポンス時間がヒストグラム表示される
- ソースコードがコンパクト
インストール
Golang環境がセットアップされているものとします。
go get github.com/rakyll/boom
実行
boom -n 回数 -c 並列度 [URL]
その他オプションはboom
コマンドで閲覧できます。
apib
- C言語製
- Apigeeエンジニアが開発
- リクエスト回数ではなく計測時間を指定する
- Warm upの時間を指定できる
- リモートサーバにデーモンを仕込んだ測定もできる
インストール
brew install apib
実行
apib -d 計測時間 -c 並列度 [URL]
その他オプションはapib
コマンドで閲覧できます。
実行結果
複数のプログラミング言語のマイクロフレームワークを用いて単純なWebAPIを作成して負荷テストツールを実行してみます。条件は以下です。
- boomの場合、並列度100で1万リクエスト
- apibの場合、並列度100で10秒
Node.js - Express
Node.jsのスタンダードなWAFです。
var express = require('express')
var app = express()
var http = require('http');
http.globalAgent.maxSockets = 50;
app.get('/', function (req, res) {
res.send('Hello')
})
var server = app.listen(3000, function () {
})
Node.js - Restify
Expressに似ていますが、Web APIに特化しています。
var restify = require('restify');
http = require('http');
http.globalAgent.maxSockets = 50;
function respond(req, res, next) {
res.send('hello');
next();
}
var server = restify.createServer();
server.get('/', respond);
server.listen(3000, function(){});
Node.js - Hapi
Walmart Lab製のWAFです。自由度の高いExpressや類似フレームワークに対して、ある程度統制を効かせてコードをクリーンに保とうという意識を感じます。WebAPIのルーティングも可読性が高いです。日本ではあまり知られていないように思いますが、小売系の他npm、Mozillaなどtech系企業のユースケースも目立ちます。
var Hapi = require('hapi');
var http = require('http');
http.globalAgent.maxSockets = 50;
var server = new Hapi.Server();
server.connection({ port: 3000 });
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello');
}
});
server.start(function () {});
Ruby - Sinatra
言わずと知れたマイクロフレームワークです。フォロワーのWAFが大量に出たのがわかるミニマルな記法です。WEBRickは遅かったためThinに変更しています。
require 'sinatra'
set :server, 'thin'
set :logging, nil
get '/' do
'Hello'
end
Ruby - Espresso
速度が売りのマイクロフレームワークです。こちらもSinatraに合わせてThinに変更しています。期待してなかったのですが検討しています。
require 'e'
class App < E
map '/'
def index
"Hello"
end
end
App.run :server => :Thin
Python - bottle
恐らくPythonで最もミニマルなWAFで個人的にもよく使っています。WSGIに対応していてアプリケーションサーバをFacebook製のTornadoに変更しました。
from bottle import route, run
@route('/', methods=['GET'])
def index():
return "Hello"
run(server='tornado', host="0.0.0.0", port=3000, debug=False)
Haskell - Scotty
Sinatraに影響を受けたHaskellのマイクロWebフレームワークです。Haskellの中では最も親しみやすい印象でした。この記法でこの性能はいい感じです。
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
main = scotty 3000 $ do
get "/" $ do
text "Hello"
Go - Martini
DIの仕組みをもったGoのマイクロフレームワークです。通常の設定だと標準出力にログが出て速度がやや遅くなったのでzishe/app.goを参考にログを消しています。Golangの人気がうかがえる速度です。
package main
import (
"log"
"github.com/go-martini/martini"
)
func init(){
martini.Env = martini.Prod
}
type myClassic struct {
*martini.Martini
martini.Router
}
func withoutLogging() *myClassic {
r := martini.NewRouter()
m := martini.New()
m.Use(martini.Recovery())
m.MapTo(r, (*martini.Routes)(nil))
m.Action(r.Handle)
return &myClassic{m, r}
}
func main() {
m := withoutLogging()
m.Get("/", func(lg *log.Logger) string {
return "Hello"
})
m.Run()
}
Go - beego
MartiniよりリッチなGoのフレームワークです。マイクロフレームワークかと思っていたらフルスタックとのこと。フルスタックかつ高性能と謳っています。
package main
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Ctx.WriteString("Hello")
}
func main() {
beego.Router("/", &MainController{})
beego.Run()
}
注意
この記事はフレームワークの性能を表すものではありません。以下のようにフレームワーク周辺領域での選択やアプリやミドルウェアの構成による影響が大きく、何をもって"フレームワークの性能"と定めるか容易には決められないからです。
- ディスクやネットワークのIO。その性能もORMやコネクタに左右されます。
- シリアライズやバリデーションなど入れ替え可能な関連ライブラリ
- PythonのWSGIやRubyのRackのようにWebサーバとWebアプリケーションフレームワーク間の統一インタフェースが提供されている場合、Webサーバの選択が性能に大きく影響します。
- ルーティングを辞書でなく配列に格納してマッチングするWAFの場合、探索の計算量がO(N)となるため登録されているルーティングが多くなると性能が劣化してしまいます。
※サンプルコードはテキストでHelloという文字列を返すだけでWeb APIとしては不完全です。
感想
WebAPIの負荷テストツールを使うことで新たなフレームワークを試すのが楽しくなりました。この記事以外にも様々なフレームワークを試した結果、記法が美しいものから魅力的なレスポンスタイムを持つものまで様々なフレームワークに出会えましたが、その中で共通して感じたのはWeb APIを実装する敷居が低くなったということです。
昨今、サービスがWeb APIを公開するのはもちろんのこと、Web APIが組み込まれたソフトウェアも目立つようになりました。ソフトウェアのWebAPIがこの記事で使ったようなマイクロフレームワークで実装されていることもあります。
このような流れが加速してサービスもソフトウェアもWeb APIを持つのが当たり前になり、誰もがコードから簡単に操作できる自由な世界が訪れるといいですね。
Enjoy!