abコマンドのベンチマークパターンを書けるab-mrubyを作ったを昨日書いたわけですが、今日はab-mrubyに対して、ベンチマーク後の結果からテストをRubyで書ける機能を追加しました。
ab-mrubyのgithubのREADMEに大体書き方は書いていますが、ここでも簡単に紹介したいと思います。
ベンチマークの実行とテスト
基本的にはabコマンドにmrubyを組み込む事で、引数のURL毎に動的にベンチマークパターンを決定したり、その後のベンチマーク結果からテストを書けるようにしています。以下のように実行します。
mオプションにベンチマークパターンファイルを、Mオプションにテストケースファイルを指定します。
ベンチマークパターンの書き方
abコマンドのベンチマークパターンを書けるab-mrubyを作ったでも詳しく書いていますが、以下のように書きます。ab-mrubyの引数に渡されるURLから動的にベンチマークパターンを決定するように書くと良いでしょう。
例えば以下のように書きます。
# Usage: ./ab-mruby -m ab-mruby.conf.rb -M ab-mruby.test.rb[http[s]://]hostname[:port]/path
#
# add_config(
# "TotalRequests" => 100, # int
# "Concurrency" => 10, # int max 20000
# "KeepAlive" => true, # true or false or nil
# "VerboseLevel" => 1, # int 1 ~ 5
# "ShowProgress" => true, # true, false or nil
# "ShowPercentile" => true, # true, false or nil
# "ShowConfidence" => true, # true, false or nil
# "WaitSocketError" => true, # true, false or nil
# "RequestTimeOut" => 30, # int sec
# "BechmarkTimelimit" => 50000, # int sec
# "WindowSize" => nil, # int byte
# "HeadMethodOnly" => false, # true, false or nil
# "Postfile" => nil, # './post.txt',
# "Putfile" => nil, # './put.txt',
# "ContentType" => nil, # 'application/x-www-form-urlencoded',
# "OutputGnuplotFile" => nil, # './gnu.txt'
# "OutputCSVFile" => nil, # './csv.txt'
# "AddCookie" => nil, # 'Apache=1234'
# "AddHeader" => 'User-Agent: ab-mruby', # 'User-Agent: test'
# "BasicAuth" => nil, # 'user:pass'
# "Proxy" => nil, # 'proxy[:port]'
# "ProxyAuth" => nil, # 'user:pass'
# "OutputHtml" => false, # true, false or nil
# "BindAddress" => nil, # 'matsumoto-r.jp'
# "SSLCipher" => 'DHE-RSA-AES128-SHA', # 'DHE-RSA-AES256-SHA' or get from [openssl ciphers -v]
# "SSLProtocol" => 'SSL3', # 'SSL2', 'SSL3', 'TLS1', 'TLS1.1', 'TLS1.2' or 'ALL'
# )
# print ab-mruby headers
print <<EOS
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby
CONFIG PHASE
======================================================================
EOS
# C側からこんなデータが得られるのでこれを元に動的にパターンを記述
p get_config("TargetURL").to_s
p get_config("TargetPort").to_s
p get_config("TargetHost").to_s
p get_config("TargetPath").to_s
p get_config("TargetisSSL").to_s
# defined config pattern
if get_config("TargetHost").to_s == "blog.example.jp"
add_config(
"TotalRequests" => 10, # int
"Concurrency" => 1, # int max 20000
"KeepAlive" => true, # true or false or nil
"VerboseLevel" => 1, # int 1 ~ 5
"ShowProgress" => true, # true, false or nil
"ShowPercentile" => true, # true, false or nil
"ShowConfidence" => true, # true, false or nil
"WaitSocketError" => true, # true, false or nil
"RequestTimeOut" => 30, # int sec
"BechmarkTimelimit" => 50000, # int sec
"WindowSize" => nil, # int byte
"HeadMethodOnly" => false, # true, false or nil
"Postfile" => nil, # './post.txt',
"Putfile" => nil, # './put.txt',
"ContentType" => nil, # 'application/x-www-form-urlencoded',
"OutputGnuplotFile" => nil, # './gnu.txt'
"OutputCSVFile" => nil, # './csv.txt'
"AddCookie" => nil, # 'Apache=1234'
"AddHeader" => 'User-Agent: ab-blog', # 'User-Agent: test'
"BasicAuth" => nil, # 'user:pass'
"Proxy" => nil, # 'proxy[:port]'
"ProxyAuth" => nil, # 'user:pass'
"OutputHtml" => false, # true, false or nil
"BindAddress" => nil, # 'matsumoto-r.jp'
"SSLCipher" => nil, # 'DHE-RSA-AES256-SHA' or get from [openssl ciphers -v]
"SSLProtocol" => nil, # 'SSL2', 'SSL3', 'TLS1', 'TLS1.1', 'TLS1.2' or 'ALL'
)
elsif get_config("TargetHost").to_s == "moblog.example.jp"
add_config(
"TotalRequests" => 20, # int
"Concurrency" => 5, # int max 20000
"KeepAlive" => false, # true or false or nil
"VerboseLevel" => 5, # int 1 ~ 5
"ShowProgress" => true, # true, false or nil
"ShowPercentile" => true, # true, false or nil
"ShowConfidence" => true, # true, false or nil
"WaitSocketError" => true, # true, false or nil
"RequestTimeOut" => 30, # int sec
"BechmarkTimelimit" => 50000, # int sec
"WindowSize" => nil, # int byte
"HeadMethodOnly" => false, # true, false or nil
"Postfile" => nil, # './post.txt',
"Putfile" => nil, # './put.txt',
"ContentType" => nil, # 'application/x-www-form-urlencoded',
"OutputGnuplotFile" => nil, # './gnu.txt'
"OutputCSVFile" => nil, # './csv.txt'
"AddCookie" => nil, # 'Apache=1234'
"AddHeader" => 'User-Agent: ab-moblog', # 'User-Agent: test'
"BasicAuth" => nil, # 'user:pass'
"Proxy" => nil, # 'proxy[:port]'
"ProxyAuth" => nil, # 'user:pass'
"OutputHtml" => false, # true, false or nil
"BindAddress" => nil, # 'matsumoto-r.jp'
"SSLCipher" => nil, # 'DHE-RSA-AES256-SHA' or get from [openssl ciphers -v]
"SSLProtocol" => nil, # 'SSL2', 'SSL3', 'TLS1', 'TLS1.1', 'TLS1.2' or 'ALL'
)
else
add_config(
"TotalRequests" => 100, # int
"Concurrency" => 10, # int max 20000
"KeepAlive" => false, # true or false or nil
"VerboseLevel" => 1, # int 1 ~ 5
)
end
if get_config("TargetisSSL")
add_config(
"SSLCipher" => 'DHE-RSA-AES128-SHA', # 'DHE-RSA-AES256-SHA' or get from [openssl ciphers -v]
"SSLProtocol" => 'SSL3', # 'SSL2', 'SSL3', 'TLS1', 'TLS1.1', 'TLS1.2' or 'ALL'
)
end
上記の詳しい説明は、abコマンドのベンチマークパターンを書けるab-mrubyを作ったを御覧ください。URLから得られる情報を元にベンチマークパターンを定義しているのがわかると思います。
テストスイートの書き方
次に、abコマンドベースのベンチマークの結果からテストを行いたい場合に、Rubyでテストケースを書いておいて、それをab-mrubyに渡す事ができます。それによって、自動的にベンチマーク結果からテストケースを元にテストを行い、即座に結果を出力してくれます。
テストスイートは例えば以下のように書くことができます。
# Usage: ./ab-mruby -m ab-mruby.conf.rb -M ab-mruby.text.rb [http[s]://]hostname[:port]/path
#
# TEST PARAMETERS
#
# "TargetURL"
# "TargetHost"
# "TargetPort"
# "TargetPath"
# "TargetisSSL"
# "TargetServerSoftware"
# "TargetServerHost"
# "TargetServerPort"
# "TargetServerSSLInfo" # if use SSL
# "TargetDocumentPath"
# "TargetDocumentLength"
# "TimeTakenforTests"
# "CompleteRequests"
# "FailedRequests"
# "ConnetcErrors" # if FailedRequests > 0
# "ReceiveErrors" # if FailedRequests > 0
# "LengthErrors" # if FailedRequests > 0
# "ExceptionsErrors" # if FailedRequests > 0
# "WriteErrors"
# "Non2xxResponses" # if Non2xxResponse > 0
# "KeepAliveRequests"
# "TotalTransferred"
# "TotalBodySent" # if body send
# "HTMLTransferred"
# "RequestPerSecond"
# "TimePerConcurrentRequest"
# "TimePerRequest"
# "TransferRate"
#
# print ab-mruby headers
print <<EOS
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby
TEST PHASE
======================================================================
EOS
module Kernel
def test_suite &blk
@@r = get_config
@@t = blk
end
def should_be val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be #{val}: #{@@r[self] == val}"
end
def should_be_over val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be over #{val}: #{@@r[self] > val}"
end
def should_be_under val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be under #{val}: #{@@r[self] < val}"
end
def test_run
@@t.call
end
end
# define test suite
test_suite do
"TargetServerHost".should_be "192.168.12.251"
"TargetServerPort".should_be 80
"TargetDocumentPath".should_be "/"
"TargetServerSoftware".should_be "Apache/2.4.4"
"FailedRequests".should_be 0
"KeepAliveRequests".should_be 0
"WriteErrors".should_be 0
"HTMLTransferred".should_be 600
"TargetDocumentLength".should_be 6
"TotalTransferred".should_be 27500
"CompleteRequests".should_be 100
"TransferRate".should_be_over 460
"TimeTakenforTests".should_be_under 0.015
"RequestPerSecond".should_be_over 1000
"TimePerRequest".should_be_under 0.5
"TimePerConcurrentRequest".should_be_under 5
"TotalBodySent".should_be 0
"ConnetcErrors".should_be 0
"ReceiveErrors".should_be 0
"LengthErrors".should_be 0
"ExceptionsErrors".should_be 0
"Non2xxResponses".should_be 0
end
test_run
まず、テストスイートを解析するようなメソッドを下記のように定義しておきます。これはあくまでサンプルなので、Ruby力の高い人はもっと良い感じのメソッドを定義すると良いと思います。基本はget_configメソッドにより、ベンチマーク結果のハッシュをC言語側であるab-mrubyからまとめて取得することができます。
def test_suite &blk
@@r = get_config
@@t = blk
end
def should_be val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be #{val}: #{@@r[self] == val}"
end
def should_be_over val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be over #{val}: #{@@r[self] > val}"
end
def should_be_under val
puts "[TEST CASE] #{self} (#{@@r[self]}) should be under #{val}: #{@@r[self] < val}"
end
def test_run
@@t.call
end
end
その上で、好きなテストケースを書きます。僕は以下のように書いてみました。
"TargetServerHost".should_be "192.168.12.251"
"TargetServerPort".should_be 80
"TargetDocumentPath".should_be "/"
"TargetServerSoftware".should_be "Apache/2.4.4"
"FailedRequests".should_be 0
"KeepAliveRequests".should_be 0
"WriteErrors".should_be 0
"HTMLTransferred".should_be 600
"TargetDocumentLength".should_be 6
"TotalTransferred".should_be 27500
"CompleteRequests".should_be 100
"TransferRate".should_be_over 460
"TimeTakenforTests".should_be_under 0.015
"RequestPerSecond".should_be_over 1000
"TimePerRequest".should_be_under 0.5
"TimePerConcurrentRequest".should_be_under 5
"TotalBodySent".should_be 0
"ConnetcErrors".should_be 0
"ReceiveErrors".should_be 0
"LengthErrors".should_be 0
"ExceptionsErrors".should_be 0
"Non2xxResponses".should_be 0
end
test_run
このように少しだけRSpecをぱくっ…インスパイアした書き方にして、テストケースを書いてみました。should_beメソッドとshould_be_over、underメソッドは、読んで時の如く、引数と同じか数値的に高いか低いを単純にテストするためのメソッドです。
実際にベンチマークとテストを実施してみる
では、上記のベンチマークパターンとテストケースをab-mrubyに渡して、HTTPのベンチマークを行なってみましょう。
以下が実行結果になります。
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby
CONFIG PHASE
======================================================================
"http://192.168.12.251/"
"80"
"192.168.12.251"
"/"
"false"
This is ApacheBench, Version 2.3-mruby <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.12.251 (be patient).....done
Server Software: Apache/2.4.4
Server Hostname: 192.168.12.251
Server Port: 80
Document Path: /
Document Length: 6 bytes
Concurrency Level: 10
Time taken for tests: 0.015 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 27500 bytes
HTML transferred: 600 bytes
Requests per second: 6839.48 [#/sec] (mean)
Time per request: 1.462 [ms] (mean)
Time per request: 0.146 [ms] (mean, across all concurrent requests)
Transfer rate: 1836.77 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 0
Processing: 1 1 0.3 1 2
Waiting: 1 1 0.3 1 2
Total: 1 1 0.3 1 3
Percentage of the requests served within a certain time (ms)
50% 1
66% 2
75% 2
80% 2
90% 2
95% 2
98% 2
99% 3
100% 3 (longest request)
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby
TEST PHASE
======================================================================
[TEST CASE] TargetServerHost (192.168.12.251) should be 192.168.12.251: true
[TEST CASE] TargetServerPort (80) should be 80: true
[TEST CASE] TargetDocumentPath (/) should be /: true
[TEST CASE] TargetServerSoftware (Apache/2.4.4) should be Apache/2.4.4: true
[TEST CASE] FailedRequests (0) should be 0: true
[TEST CASE] KeepAliveRequests (0) should be 0: true
[TEST CASE] WriteErrors (0) should be 0: true
[TEST CASE] HTMLTransferred (600) should be 600: true
[TEST CASE] TargetDocumentLength (6) should be 6: true
[TEST CASE] TotalTransferred (27500) should be 27500: true
[TEST CASE] CompleteRequests (100) should be 100: true
[TEST CASE] TransferRate (1836.7737329) should be over 460: true
[TEST CASE] TimeTakenforTests (0.014621) should be under 0.015: true
[TEST CASE] RequestPerSecond (6839.4774639) should be over 1000: true
[TEST CASE] TimePerRequest (0.14621) should be under 0.5: true
[TEST CASE] TimePerConcurrentRequest (1.4621) should be under 5: true
[TEST CASE] TotalBodySent (0) should be 0: true
[TEST CASE] ConnetcErrors (0) should be 0: true
[TEST CASE] ReceiveErrors (0) should be 0: true
[TEST CASE] LengthErrors (0) should be 0: true
[TEST CASE] ExceptionsErrors (0) should be 0: true
[TEST CASE] Non2xxResponses (0) should be 0: true
add_config(
"TotalRequests" => 100, # int
"Concurrency" => 10, # int max 20000
"KeepAlive" => false, # true or false or nil
"VerboseLevel" => 1, # int 1 ~ 5
)
end
ちゃんと上記の通り、ベンチマークをしていることがベンチマーク結果から分かります。さらにその結果を用いて、テストを行なっている事がわかります。今回は全てtrueになって、テストが全て通ったことになりますね!
また、今回のテストは一番シンプルなものにしているので、get_configで得られるパラメータやベンチマーク結果の値から、URLやパス情報を元に動的にテストケースを書いておくことも可能なので、テストの書き方にも夢が広がると思います。
最後に
このように、HTTPのベンチマークにおいて、汎用的にパターンとテストを記述できるツールを探していいたのですが、あまりシンプルで良いものがなかったので自分で作ってみました。
また、abコマンドをベースにしていることから、abでベンチマークをするような状況においては、同様にab-mrubyを使えるので、今後HTTPサーバのテストも捗るのではないでしょうか。
とりあえず、既存のabよりは大分使いやすく、ベンチマークパターンやテストの可読性向上、ベンチマークパターンとベンチマークとテストの連携が楽になったので、abを使うような場面ではab-mrubyを使っていこうと思います。
0 Comments.