internethakai 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +53 -0
- data/README +177 -0
- data/bin/hakaigen +10 -0
- data/bin/internethakai +9 -0
- data/internethakai.gemspec +27 -0
- data/lib/internethakai.rb +26 -0
- data/lib/internethakai.rb.~1~ +25 -0
- data/lib/internethakai/action.rb +403 -0
- data/lib/internethakai/client_handler.rb +175 -0
- data/lib/internethakai/client_queue.rb +27 -0
- data/lib/internethakai/concurrency_manager.rb +333 -0
- data/lib/internethakai/concurrency_manager.rb.~1~ +331 -0
- data/lib/internethakai/core.rb +146 -0
- data/lib/internethakai/executor.rb +129 -0
- data/lib/internethakai/generator.rb +32 -0
- data/lib/internethakai/hakairev.rb +119 -0
- data/lib/internethakai/hakairev/executor.rb +43 -0
- data/lib/internethakai/hakairev/http_client.rb +362 -0
- data/lib/internethakai/hakairev/http_client.rb.~1~ +371 -0
- data/lib/internethakai/hakairev/monkey.rb +70 -0
- data/lib/internethakai/hakairev/revpipe.rb +39 -0
- data/lib/internethakai/hakairev/task.rb +39 -0
- data/lib/internethakai/hakairev/time_register.rb +116 -0
- data/lib/internethakai/hakairev/timer_factory.rb +38 -0
- data/lib/internethakai/http_client.rb +120 -0
- data/lib/internethakai/logger.rb +52 -0
- data/lib/internethakai/main.rb +90 -0
- data/lib/internethakai/reporter.rb +143 -0
- data/lib/internethakai/response.rb +65 -0
- data/lib/internethakai/scenario.rb +98 -0
- data/lib/internethakai/scenario.tmpl +58 -0
- data/lib/internethakai/scenario_builder.rb +183 -0
- data/lib/internethakai/scenario_handler.rb +91 -0
- data/lib/internethakai/time_register.rb +53 -0
- data/lib/internethakai/util.rb +130 -0
- metadata +134 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
class Reporter < BaseHandler
|
3
|
+
UNIQUE_BY_THREAD = false
|
4
|
+
def run record
|
5
|
+
@config = BaseHandler.get_config
|
6
|
+
@logger = BaseHandler::get_handler(@config["logger"])
|
7
|
+
end
|
8
|
+
def set_dir dir
|
9
|
+
@dir = dir
|
10
|
+
end
|
11
|
+
def init_filename fname
|
12
|
+
@filename = fname
|
13
|
+
end
|
14
|
+
def get_filename
|
15
|
+
@filename
|
16
|
+
end
|
17
|
+
end
|
18
|
+
class PStoreReporter < Reporter
|
19
|
+
require 'pstore'
|
20
|
+
def run record
|
21
|
+
super
|
22
|
+
return unless record
|
23
|
+
fpath = File::join(@dir, @filename)
|
24
|
+
@logger.run("save to #{fpath}\n", 3)
|
25
|
+
db = PStore::new(fpath)
|
26
|
+
db.transaction do
|
27
|
+
db["result"] = ResponseRecord::new unless db.root?('result')
|
28
|
+
db['result'] += record
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
#TSV形式で保存するクラス
|
33
|
+
class TsvReporter < Reporter
|
34
|
+
UNIQUE_BY_THREAD = false
|
35
|
+
def init_filename starttime
|
36
|
+
@filename = "report_#{starttime}.tsv"
|
37
|
+
end
|
38
|
+
def run record
|
39
|
+
super
|
40
|
+
if not record
|
41
|
+
@logger.run("no rocord\n", 2)
|
42
|
+
exit -1
|
43
|
+
end
|
44
|
+
keys = record.keys.to_a.sort
|
45
|
+
strResultTSV = "Path\tCount\tAvgResponse(ms)\tMinResponse(ms)\tMaxResponse(ms)\tAvgContentLength\tError%\tHTTP_STATUS_CODE\ttime(ms)\n"
|
46
|
+
|
47
|
+
rank_avg_time = []
|
48
|
+
sum_avg_time = 0
|
49
|
+
rank_error_per = []
|
50
|
+
sum_error = 0
|
51
|
+
sum_access = 0
|
52
|
+
|
53
|
+
if keys.nil?
|
54
|
+
@logger.run("cannot show report\n", 2)
|
55
|
+
exit -1
|
56
|
+
end
|
57
|
+
keys.to_a.each do |key|
|
58
|
+
v = record[key]
|
59
|
+
next if v[:accesscount] == 0
|
60
|
+
avg_res = v[:totaltime] * 1000.0 / v[:accesscount]
|
61
|
+
error_per = (v[:errorcount].to_f / v[:accesscount])*100
|
62
|
+
|
63
|
+
rank_avg_time << [key, avg_res, v[:accesscount], v[:min]*1000.0]
|
64
|
+
sum_avg_time += v[:totaltime] * 1000.0
|
65
|
+
rank_error_per << [key, error_per, v[:accesscount]]
|
66
|
+
sum_error += v[:errorcount]
|
67
|
+
sum_access += v[:accesscount]
|
68
|
+
|
69
|
+
strResultTSV +=
|
70
|
+
key + "\t" + # URL
|
71
|
+
v[:accesscount].to_s + "\t" + # COUNT
|
72
|
+
format( '%f', avg_res ) + "\t" + # AVG
|
73
|
+
format('%f', v[:min].to_f*1000) + "\t" +
|
74
|
+
format('%f', v[:max].to_f*1000) + "\t" +
|
75
|
+
format( '%d', v[:size] / v[:accesscount] ) + "\t" + # AVG
|
76
|
+
(error_per).to_s + "\t" # ERROR%
|
77
|
+
status_code = []
|
78
|
+
res_time =[]
|
79
|
+
v.keys.each do |v_key|
|
80
|
+
if(v_key.to_s =~ /^status:/)
|
81
|
+
status_code << "#{v_key}: #{v[v_key]}"
|
82
|
+
end
|
83
|
+
if(v_key.to_s =~ /^time:/)
|
84
|
+
res_time << "#{v_key}: #{v[v_key]}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
strResultTSV += status_code.sort.join(",") + "\t" # HTTP_STAT
|
88
|
+
strResultTSV += res_time.sort.join(",") # RESPONSE TIME
|
89
|
+
strResultTSV += "\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
if @config["save_report"]
|
93
|
+
save strResultTSV
|
94
|
+
fpath = File::join(@dir, @filename)
|
95
|
+
@logger.run("save to:"+fpath+"\n", 2)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
#ranking
|
100
|
+
rank_error_per = rank_error_per.sort_by{|p|p[1]}.reverse.slice(0,@config["ranking"]).to_a
|
101
|
+
rank_avg_time = rank_avg_time.sort_by{|p|p[1]}.reverse.slice(0,@config["ranking"]).to_a
|
102
|
+
rank_min_time = rank_avg_time.sort_by{|p|p[3]}.reverse.slice(0,@config["ranking"]).to_a
|
103
|
+
|
104
|
+
@logger.run("MinResponse Worst:\n", 2)
|
105
|
+
rank = 1
|
106
|
+
rank_min_time.each do |p|
|
107
|
+
@logger.run("\t#{rank}: #{p[0]} #{p[3]} #{p[2]}\n", 2)
|
108
|
+
rank += 1
|
109
|
+
end
|
110
|
+
@logger.run("AvgResponse Worst:\n", 2)
|
111
|
+
rank = 1
|
112
|
+
rank_avg_time.each do |p|
|
113
|
+
@logger.run("\t#{rank}: #{p[0]} #{p[1]} #{p[2]}\n", 2)
|
114
|
+
rank += 1
|
115
|
+
end
|
116
|
+
@logger.run("AvgResponse Total:\n", 2)
|
117
|
+
@logger.run("\t#{sum_avg_time.to_f / sum_access}\n", 2)
|
118
|
+
|
119
|
+
@logger.run("ErrorRate Worst:\n", 2)
|
120
|
+
rank = 1
|
121
|
+
rank_error_per.each do |p|
|
122
|
+
@logger.run("\t#{rank}: #{p[0]} #{p[1]} #{p[2]}\n", 2)
|
123
|
+
rank += 1
|
124
|
+
end
|
125
|
+
@logger.run("ErrorRate Total:\n", 2)
|
126
|
+
@logger.run("\t#{(sum_error.to_f / sum_access)*100.0}\n", 2)
|
127
|
+
|
128
|
+
if(@config["show_report"])
|
129
|
+
@logger.run(strResultTSV, 2)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
def save str
|
133
|
+
begin
|
134
|
+
fpath = File::join(@dir, @filename)
|
135
|
+
File::open(fpath, "w") do |io|
|
136
|
+
io.print str
|
137
|
+
end
|
138
|
+
rescue
|
139
|
+
raise $!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
#http response
|
3
|
+
class ResponseObject
|
4
|
+
def self::create_from_nethttp obj
|
5
|
+
r = self::new
|
6
|
+
r.body = obj.body.to_s
|
7
|
+
r.status = obj.code.to_i
|
8
|
+
r.content_type = obj['content-type']
|
9
|
+
r.location = obj['location'] if obj['location']
|
10
|
+
r.cookie = obj['set-cookie']
|
11
|
+
return r
|
12
|
+
end
|
13
|
+
def initialize
|
14
|
+
@header = nil
|
15
|
+
@status = 0
|
16
|
+
@time = 0
|
17
|
+
@content_type = ''
|
18
|
+
@location = nil
|
19
|
+
@body = ''
|
20
|
+
@cookie = nil
|
21
|
+
end
|
22
|
+
attr_accessor :body, :header, :status, :time, :content_type, :location, :cookie
|
23
|
+
end
|
24
|
+
#レスポンスタイム記録クラス
|
25
|
+
class ResponseRecord < Hash
|
26
|
+
## 参照
|
27
|
+
def []( key )
|
28
|
+
if (! self.has_key?( key ))
|
29
|
+
self[key] = {
|
30
|
+
:totaltime => 0,
|
31
|
+
:accesscount => 0,
|
32
|
+
:errorcount => 0,
|
33
|
+
:size => 0,
|
34
|
+
:min => nil,
|
35
|
+
:max => nil
|
36
|
+
}
|
37
|
+
end
|
38
|
+
return( super(key) )
|
39
|
+
end
|
40
|
+
# 演算
|
41
|
+
def +(other)
|
42
|
+
other.each do |key, value|
|
43
|
+
s = self[key]
|
44
|
+
s[:totaltime] += value[:totaltime]
|
45
|
+
s[:accesscount] += value[:accesscount]
|
46
|
+
s[:errorcount] += value[:errorcount]
|
47
|
+
s[:size] += value[:size]
|
48
|
+
if s[:min].nil?
|
49
|
+
s[:min] = value[:min]
|
50
|
+
s[:max] = value[:max]
|
51
|
+
else
|
52
|
+
s[:min] = (s[:min] < value[:min]) ? s[:min] : value[:min] unless value[:min].nil?
|
53
|
+
s[:max] = (s[:max] > value[:max]) ? s[:max] : value[:max] unless value[:max].nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
value.keys.each do |v_key|
|
57
|
+
if(v_key.to_s =~ /status:/ || v_key.to_s =~ /time:/)
|
58
|
+
s[v_key] = s[v_key].to_i + value[v_key]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
return self
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
# = シナリオ
|
3
|
+
class Scenario
|
4
|
+
def initialize(opt)
|
5
|
+
@config = opt
|
6
|
+
@size = 0
|
7
|
+
@actions_size
|
8
|
+
@performance_id = nil
|
9
|
+
@scenario_id = nil
|
10
|
+
@loop = @config['loop']
|
11
|
+
@loop_org = @loop
|
12
|
+
digit = @loop.to_s.size
|
13
|
+
@tmpl = "%0#{digit}d"
|
14
|
+
@request_pool = BaseHandler::get_handler(@config['request_pool'])
|
15
|
+
|
16
|
+
#@page_size = [10, @loop].min
|
17
|
+
@page_size = 1
|
18
|
+
@page = (@loop.quo(@page_size)).ceil
|
19
|
+
@page_counter = @page
|
20
|
+
@vars = {}
|
21
|
+
@cookie = {}
|
22
|
+
end
|
23
|
+
attr_reader :actions_size
|
24
|
+
attr_accessor :performance_id, :scenario_id, :vars, :cookie
|
25
|
+
def size
|
26
|
+
@size
|
27
|
+
end
|
28
|
+
def init
|
29
|
+
@performance_id = (@scenario_id.to_s + sprintf(@tmpl, @loop)).to_i
|
30
|
+
act = @commands.shift
|
31
|
+
act.performance_id = @performance_id
|
32
|
+
@request_pool.add(act)
|
33
|
+
end
|
34
|
+
def next
|
35
|
+
@action_counter -= 1
|
36
|
+
#puts "action: #{@action_counter}"
|
37
|
+
#puts "loop: #{@loop}"
|
38
|
+
if @action_counter == 0
|
39
|
+
@loop -= 1
|
40
|
+
#これで最後
|
41
|
+
return if @loop == 0
|
42
|
+
@action_counter = @actions_size
|
43
|
+
@performance_id = (@scenario_id.to_s + sprintf(@tmpl, @loop)).to_i
|
44
|
+
end
|
45
|
+
if @commands.empty?
|
46
|
+
#ループが残っていればまた最初から
|
47
|
+
@commands = @commands_org.clone
|
48
|
+
@cookie = {}
|
49
|
+
@vars = {}
|
50
|
+
end
|
51
|
+
act = @commands.shift
|
52
|
+
act.performance_id = @performance_id
|
53
|
+
@request_pool.add(act)
|
54
|
+
end
|
55
|
+
def each &block
|
56
|
+
@commands_org.each do |cm|
|
57
|
+
block.call(cm)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def create_scenario
|
61
|
+
@commands = []
|
62
|
+
@performance_id = (@scenario_id.to_s + sprintf(@tmpl, @loop)).to_i
|
63
|
+
@actions_size = @config['actions'].inject(0){|sum, i|
|
64
|
+
times = i['times']
|
65
|
+
sum + times
|
66
|
+
}
|
67
|
+
baseconfig = @config.clone
|
68
|
+
baseconfig.delete('actions')
|
69
|
+
1.upto(@page_size){
|
70
|
+
@config["actions"].each do |actconfig|
|
71
|
+
actconfig = baseconfig.merge(actconfig)
|
72
|
+
times = actconfig['times']
|
73
|
+
command = actconfig['class_obj']
|
74
|
+
1.upto(times) do
|
75
|
+
actconfignew = actconfig.clone
|
76
|
+
obj = command.new(actconfignew, self)
|
77
|
+
obj.set_wait if(actconfignew['wait'])
|
78
|
+
@commands << obj
|
79
|
+
end
|
80
|
+
end
|
81
|
+
}
|
82
|
+
@action_counter = @actions_size
|
83
|
+
@commands_org = @commands.clone
|
84
|
+
@size = @actions_size * @loop_org
|
85
|
+
end
|
86
|
+
def set_cookie cookie
|
87
|
+
return unless cookie
|
88
|
+
k, v = cookie[0...cookie.index(';')].split('=')
|
89
|
+
@cookie[k] = v if k && v
|
90
|
+
end
|
91
|
+
def create_act idx
|
92
|
+
actconf = @actconfigs[idx]
|
93
|
+
obj = actconf['class_obj']::new(actconf)
|
94
|
+
obj.set_scenario(self)
|
95
|
+
obj
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
loop:
|
2
|
+
1
|
3
|
+
max_request:
|
4
|
+
1
|
5
|
+
max_scenario:
|
6
|
+
1
|
7
|
+
log_level:
|
8
|
+
2
|
9
|
+
encoding: Shift_JIS
|
10
|
+
ranking: 20
|
11
|
+
timeout: 5
|
12
|
+
show_report: true
|
13
|
+
save_report: false
|
14
|
+
|
15
|
+
## 負荷試験対象のサイトに変更してください
|
16
|
+
domain:
|
17
|
+
"<%= domain %>"
|
18
|
+
|
19
|
+
|
20
|
+
<% if social %>
|
21
|
+
## ソーシャルアプリの場合コメントアウト
|
22
|
+
client_handler: "SocialClientHandler"
|
23
|
+
opensocial_id_path: #ユーザーidを書いたファイルを用意してください
|
24
|
+
./userids
|
25
|
+
opensocial_app_id:
|
26
|
+
yourapp
|
27
|
+
<% else %>
|
28
|
+
## ソーシャルアプリの場合コメントアウト
|
29
|
+
#client_handler: "SocialClientHandler"
|
30
|
+
#opensocial_id_path: #ユーザーidを書いたファイルを用意してください
|
31
|
+
# ./userids
|
32
|
+
#opensocial_app_id:
|
33
|
+
# app
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
## ユーザーエージェントを変更したい場合、コメントアウト
|
37
|
+
#user_agent:
|
38
|
+
# 'Mozilla/4.0 (compatible; MSIE 4.0; MSN 2.5; Windows 95)'
|
39
|
+
|
40
|
+
|
41
|
+
## fork して使用する場合コメントアウト
|
42
|
+
#max_process: 4
|
43
|
+
|
44
|
+
|
45
|
+
#以下にアクション(シナリオ)を書きます
|
46
|
+
actions:
|
47
|
+
-
|
48
|
+
path: /
|
49
|
+
## example
|
50
|
+
# -
|
51
|
+
# path: /somepath
|
52
|
+
# -
|
53
|
+
# path: /somepath
|
54
|
+
# method: POST
|
55
|
+
# post_params:
|
56
|
+
# key1: hoge
|
57
|
+
# key2: fuga
|
58
|
+
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
class ScenarioBuilder < BaseHandler
|
3
|
+
UNIQUE_BY_THREAD = false
|
4
|
+
require 'socket'
|
5
|
+
require 'yaml'
|
6
|
+
DEFAULT_CONFIG = {
|
7
|
+
"loop" => 1,
|
8
|
+
"rev" => true,
|
9
|
+
"max_scenario" => 1,
|
10
|
+
"max_request" => 1,
|
11
|
+
"log_level" => 3,
|
12
|
+
"ranking" => 5,
|
13
|
+
"follow_redirect" => true,
|
14
|
+
"queue" => "SimpleQueue",
|
15
|
+
"action_handler" => "RequestAction",
|
16
|
+
"client_handler" => "ClientHandler",
|
17
|
+
"response_handler" => "TimeRegister",
|
18
|
+
"request_pool" => "RequestPool",
|
19
|
+
"http_client" => "HttpClient",
|
20
|
+
"scenario_executer" => "ScenarioExecuter",
|
21
|
+
"logger" => "Logger",
|
22
|
+
"encoding" => "Shift_JIS",
|
23
|
+
"save_report" => false,
|
24
|
+
"reporter" => "TsvReporter",
|
25
|
+
"show_report" => false,
|
26
|
+
"sleep" => false,
|
27
|
+
"timeout" => 10,
|
28
|
+
"actions" => [],
|
29
|
+
}
|
30
|
+
def get_config
|
31
|
+
@config
|
32
|
+
end
|
33
|
+
def prepare_config
|
34
|
+
baseconfig = @config
|
35
|
+
basehost, baseport, = Util::parse_url(baseconfig['domain'])
|
36
|
+
basehostaddress = IPSocket::getaddress(basehost)
|
37
|
+
baseconfig['host'] = basehostaddress
|
38
|
+
baseconfig['host_name'] = basehost unless baseconfig['host_name']
|
39
|
+
baseconfig['port'] = baseport
|
40
|
+
baseconfig['encoding'] ||= 'UTF-8'
|
41
|
+
|
42
|
+
#後方互換性のため
|
43
|
+
if baseconfig.has_key?('fork') && baseconfig['fork'].has_key?('max_process') && baseconfig['fork']['max_process'] > 1 && !baseconfig.has_key?('max_process')
|
44
|
+
baseconfig['max_process'] = baseconfig['fork']['max_process']
|
45
|
+
else
|
46
|
+
baseconfig['max_process'] ||= 1
|
47
|
+
end
|
48
|
+
#後方互換性のため
|
49
|
+
if baseconfig.has_key?('concurrency') && baseconfig['max_scenario'] == 1
|
50
|
+
baseconfig['max_scenario'] = baseconfig['concurrency']
|
51
|
+
end
|
52
|
+
|
53
|
+
headers = baseconfig.has_key?('header') ? baseconfig['header'] : Hash::new
|
54
|
+
headers['User-Agent'] = baseconfig['user_agent'] if baseconfig.has_key?('user_agent')
|
55
|
+
headers['Host'] = baseconfig['host_name']
|
56
|
+
baseconfig['header'] = headers
|
57
|
+
hosts = []
|
58
|
+
hosts << [basehostaddress, basehost, baseport]
|
59
|
+
hosts_cache = {}
|
60
|
+
hosts_cache[hosts[0].join(':')] = true
|
61
|
+
idx = 0
|
62
|
+
|
63
|
+
base = baseconfig.clone
|
64
|
+
base.delete('actions')
|
65
|
+
actions = []
|
66
|
+
@config["actions"].each do |actconfig|
|
67
|
+
if actconfig.is_a? String
|
68
|
+
path = actconfig
|
69
|
+
actconfig = Hash::new
|
70
|
+
actconfig['path'] = path
|
71
|
+
end
|
72
|
+
url = actconfig['path']
|
73
|
+
actconfig['path'] = url
|
74
|
+
actconfig['method'] = 'GET' unless actconfig.has_key? 'method'
|
75
|
+
actconfig['class'] = baseconfig['action_handler'] unless actconfig.has_key? 'class'
|
76
|
+
classname = actconfig["class"]
|
77
|
+
command = BaseHandler::get_class(classname)
|
78
|
+
raise "invalid class" unless command < BaseAction
|
79
|
+
actconfig['class_obj'] = command
|
80
|
+
|
81
|
+
if (!url.match( /^http:\/\// ))
|
82
|
+
url = baseconfig['domain'].to_s + url.to_s
|
83
|
+
actconfig['host'] = basehostaddress
|
84
|
+
actconfig['host_name'] = basehost
|
85
|
+
actconfig['port'] = baseport
|
86
|
+
else
|
87
|
+
host, port, path = Util::parse_url(url)
|
88
|
+
#名前解決
|
89
|
+
address = IPSocket::getaddress(host)
|
90
|
+
actconfig['host'] = address
|
91
|
+
actconfig['path'] = path
|
92
|
+
actconfig['host_name'] = host unless baseconfig['host_name']
|
93
|
+
actconfig['port'] = port
|
94
|
+
info = [address, host, port]
|
95
|
+
headers = actconfig.has_key?('header') ? actconfig['header'] : Hash::new
|
96
|
+
headers['User-Agent'] = baseconfig['user_agent'] if baseconfig.has_key?('user_agent')
|
97
|
+
headers['Host'] = actconfig['host_name']
|
98
|
+
actconfig['header'] = headers
|
99
|
+
key = info.join(':')
|
100
|
+
unless hosts_cache.has_key?(key)
|
101
|
+
hosts << info
|
102
|
+
hosts_cache[key] = true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
if(actconfig.has_key?('post_params'))
|
106
|
+
actconfig['post_string'] = Util::hash2poststring(actconfig['post_params'])
|
107
|
+
end
|
108
|
+
unless actconfig.has_key?('type')
|
109
|
+
actconfig['type'] = actconfig['path'].gsub( /\?.*$/, '' ).gsub( /[#;].*/, '' )
|
110
|
+
end
|
111
|
+
if actconfig.has_key?('expr')
|
112
|
+
if RUBY_VERSION >= "1.9"
|
113
|
+
s = actconfig['expr'].encode(baseconfig['encoding'])
|
114
|
+
actconfig['compiled_expr'] = Regexp::compile(s)
|
115
|
+
else
|
116
|
+
actconfig['compiled_expr'] = Regexp::compile(actconfig['expr'])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
actconfig['url'] = url
|
120
|
+
time = actconfig['times'] || 1
|
121
|
+
actconfig['times'] = time unless actconfig.has_key?('times')
|
122
|
+
idx += 1
|
123
|
+
actions << actconfig
|
124
|
+
end
|
125
|
+
scenario_size = actions.inject(0){|sum, i|
|
126
|
+
sum + i['times']
|
127
|
+
}
|
128
|
+
@config['scenario_size'] = scenario_size
|
129
|
+
@config['actions'] = actions
|
130
|
+
@config['hosts'] = hosts
|
131
|
+
#concurrency はデフォルトでスレッドと同じ
|
132
|
+
if !@config.has_key?('max_scenario') || @config['max_scenario'] < @config['max_request']
|
133
|
+
@config['max_scenario'] = @config['max_request']
|
134
|
+
end
|
135
|
+
@config['max_scenario'] = 1 if @config['max_scenario'] <= 0
|
136
|
+
|
137
|
+
@config['max_scenario_show'] = @config['max_scenario']
|
138
|
+
@config['max_request_show'] = @config['max_request']
|
139
|
+
if @config['max_process'] > 1
|
140
|
+
#fork する場合は、concurrency をプロセス数で割る
|
141
|
+
process = @config['max_process']
|
142
|
+
@config['max_process'] = process
|
143
|
+
@config['max_scenario'] = (@config['max_scenario'] / process).round
|
144
|
+
@config['max_scenario'] = 1 if @config['max_scenario'] <= 0
|
145
|
+
@config['max_request'] = (@config['max_request']/ process).round
|
146
|
+
@config['max_request'] = 1 if @config['max_request'] <= 0
|
147
|
+
end
|
148
|
+
ch = BaseHandler::get_class(@config["client_handler"])
|
149
|
+
@config = ch::on_config_load(@config)
|
150
|
+
end
|
151
|
+
def load path
|
152
|
+
begin
|
153
|
+
basedir = File::expand_path(File::dirname(path))
|
154
|
+
str = File::open(path){|io|io.read}
|
155
|
+
load_config(str, basedir)
|
156
|
+
rescue
|
157
|
+
raise $!
|
158
|
+
end
|
159
|
+
end
|
160
|
+
def load_config str, basedir='.'
|
161
|
+
default_config = DEFAULT_CONFIG
|
162
|
+
if(RUBY_VERSION.to_f >= 1.9)
|
163
|
+
str.force_encoding('UTF-8')
|
164
|
+
end
|
165
|
+
if str.include?('%%BASEDIR%%')
|
166
|
+
puts "WARNING: %%BASEDIR%% is obsolete"
|
167
|
+
str.gsub!('%%BASEDIR%%', basedir)
|
168
|
+
end
|
169
|
+
@config = ::YAML::load(str)
|
170
|
+
raise "invalid file" unless @config
|
171
|
+
@config = default_config.merge(@config)
|
172
|
+
prepare_config
|
173
|
+
if(@config["require"])
|
174
|
+
begin
|
175
|
+
require @config["require"]
|
176
|
+
rescue
|
177
|
+
@logger.run("class load error: #{@config['require']}", 1)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
@config
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|