internethakai 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,331 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
class ConcurrencyManager
|
3
|
+
require 'thread'
|
4
|
+
def initialize
|
5
|
+
@fork_mode = false
|
6
|
+
@rev_mode = false
|
7
|
+
@process = 0
|
8
|
+
@suffix = ''
|
9
|
+
@mutex = nil
|
10
|
+
end
|
11
|
+
attr_accessor :suffix, :process
|
12
|
+
def prepare_rev
|
13
|
+
return if @rev_mode #2回実行しないように
|
14
|
+
require 'rubygems'
|
15
|
+
require 'rev'
|
16
|
+
require 'internethakai/hakairev'
|
17
|
+
#TimeRegistProcess::run
|
18
|
+
@config['http_client'] = 'RevHttpClient'
|
19
|
+
@config['scenario_executer'] = 'NonBlockScenarioExecuter'
|
20
|
+
@config['request_pool'] = 'RequestPoolNonThread'
|
21
|
+
@config['queue'] = 'TaskQueue'
|
22
|
+
#@config['response_handler'] = 'PipeTimeRegister'
|
23
|
+
@config['response_handler'] = 'RevTimeRegister'
|
24
|
+
@config['scan']
|
25
|
+
@rev_mode = true
|
26
|
+
end
|
27
|
+
def prepare_fork
|
28
|
+
return if @fork_mode
|
29
|
+
@concurrency = @config['concurrency']
|
30
|
+
@process_max = @config['fork']['max_process'] || 2
|
31
|
+
@fork_mode = true
|
32
|
+
end
|
33
|
+
def run config, basepath, starttime
|
34
|
+
@config = config
|
35
|
+
@scenario_handler = BaseHandler::get_handler("ScenarioHandler")
|
36
|
+
@failure = false
|
37
|
+
@basepath = basepath
|
38
|
+
BaseHandler::set_config(@config)
|
39
|
+
@logger = BaseHandler::get_handler(@config["logger"])
|
40
|
+
@threads = {}
|
41
|
+
|
42
|
+
@concurrency = @config['concurrency']
|
43
|
+
if @config['rev']
|
44
|
+
prepare_rev
|
45
|
+
end
|
46
|
+
if @config['fork']
|
47
|
+
prepare_fork
|
48
|
+
end
|
49
|
+
@register = BaseHandler::get_handler(@config['response_handler'])
|
50
|
+
reporter = BaseHandler::get_handler(@config["reporter"])
|
51
|
+
|
52
|
+
digit = @config['max_request'].to_s.size
|
53
|
+
@id_tmpl = "%0#{digit}d"
|
54
|
+
if @config['fork']
|
55
|
+
run_fork
|
56
|
+
else
|
57
|
+
run_process
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def run_process
|
61
|
+
#並列数だけシナリオのコピーをつくっておく
|
62
|
+
@config['request_pool'] = 'RequestPoolNonThread' if @config["max_request"] <= 1
|
63
|
+
@scenarios = []
|
64
|
+
1.upto(@concurrency) do
|
65
|
+
@scenarios << @scenario_handler.create_scenario(@suffix)
|
66
|
+
end
|
67
|
+
@scenario_handler.on_scenarios_created
|
68
|
+
$REQUEST_COUNT = 0
|
69
|
+
begin
|
70
|
+
if @rev_mode
|
71
|
+
rev_start
|
72
|
+
elsif @config["max_request"] <= 1
|
73
|
+
single_start
|
74
|
+
else
|
75
|
+
thread_start
|
76
|
+
end
|
77
|
+
rescue SystemExit
|
78
|
+
return
|
79
|
+
rescue Exception => e
|
80
|
+
raise e
|
81
|
+
puts "error: #{e.message}"
|
82
|
+
puts e.backtrace[0]
|
83
|
+
if @fork_mode
|
84
|
+
#死ぬ前に親に連絡
|
85
|
+
Process::kill(:USR1, Process::ppid)
|
86
|
+
else
|
87
|
+
report_collect
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
def single_start
|
92
|
+
th_cnt = 1
|
93
|
+
@threads[1] = Thread::current
|
94
|
+
@scenarios.each do |sc|
|
95
|
+
sc.init
|
96
|
+
end
|
97
|
+
#シングルスレッドのときは先にプールにためておく
|
98
|
+
run_executer th_cnt
|
99
|
+
if @failure
|
100
|
+
page_check
|
101
|
+
exit 1
|
102
|
+
else
|
103
|
+
exit 0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
def page_check
|
107
|
+
keys = @result_record.keys
|
108
|
+
pages = []
|
109
|
+
keys.each do |key|
|
110
|
+
err = @result_record[key][:errorcount] || 0
|
111
|
+
if(err > 0)
|
112
|
+
pages << key
|
113
|
+
end
|
114
|
+
end
|
115
|
+
unless pages.empty?
|
116
|
+
@logger.run("ERROR URL: #{pages.join(',')}\n", 2)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
def thread_start
|
120
|
+
@mutex = Mutex::new
|
121
|
+
@request_pool = BaseHandler::get_handler(@config['request_pool'])
|
122
|
+
@cv = ConditionVariable::new
|
123
|
+
|
124
|
+
@scenarios.each do |sc|
|
125
|
+
sc.init
|
126
|
+
end
|
127
|
+
@scenario_handler.set_on_turn_complete &method(:on_complete)
|
128
|
+
1.upto(@config["max_request"]) do |th_cnt|
|
129
|
+
@threads[th_cnt] = Thread::new(th_cnt) do |th_cnt|
|
130
|
+
run_executer th_cnt
|
131
|
+
end
|
132
|
+
end
|
133
|
+
@threads[:main] = Thread::current
|
134
|
+
@mutex.synchronize{
|
135
|
+
@cv.wait(@mutex)
|
136
|
+
}
|
137
|
+
@threads.each do |k,th|
|
138
|
+
th.kill if th!=Thread::current && k!=:main
|
139
|
+
end
|
140
|
+
if @failure
|
141
|
+
exit 1
|
142
|
+
else
|
143
|
+
exit 0
|
144
|
+
end
|
145
|
+
end
|
146
|
+
def run_executer th_cnt
|
147
|
+
th_id = (sprintf(@id_tmpl, th_cnt) + @suffix).to_i
|
148
|
+
report = BaseHandler::get_handler(@config["response_handler"])
|
149
|
+
Thread.current[:result] = report
|
150
|
+
BaseHandler::set_thread_id(th_id)
|
151
|
+
cls = BaseHandler::get_class(@config['scenario_executer'])
|
152
|
+
@scenario_handler.set_on_turn_complete(&@register.method(:exec))
|
153
|
+
exec = cls::new
|
154
|
+
exec.set_on_complete &method(:on_complete)
|
155
|
+
@starttime = Time::now
|
156
|
+
exec.run
|
157
|
+
end
|
158
|
+
# = 1プロセス終了時に呼ばれる
|
159
|
+
def on_complete
|
160
|
+
total = Time::now - @starttime
|
161
|
+
@request_pool = BaseHandler::get_handler(@config['request_pool'])
|
162
|
+
@request_pool.close
|
163
|
+
#puts "thread collect start"
|
164
|
+
thread_collect
|
165
|
+
#puts "thread collect end"
|
166
|
+
report_collect
|
167
|
+
#puts "report collect end"
|
168
|
+
@logger.run("TotalTime: " + total.to_s + "\n", 2) unless @fork_mode
|
169
|
+
@failure = true if $error
|
170
|
+
@cv.signal if @cv
|
171
|
+
end
|
172
|
+
def create_id loop_c, th_c
|
173
|
+
(loop_c.to_s + sprintf(@id_tmpl, th_c) + @suffix).to_i
|
174
|
+
end
|
175
|
+
def thread_collect
|
176
|
+
count = 0
|
177
|
+
status = '.'
|
178
|
+
flag = false
|
179
|
+
count = 0
|
180
|
+
@threads.each do |k, th|
|
181
|
+
if(th[:result])
|
182
|
+
status = 't'
|
183
|
+
ect = 0
|
184
|
+
th[:result].get_record.each do |name,res|
|
185
|
+
ect += 1 if res[:errorcount] > 0
|
186
|
+
end
|
187
|
+
if ect > 0
|
188
|
+
status = 'e'
|
189
|
+
flag = true unless flag
|
190
|
+
end
|
191
|
+
#th.kill if th!=Thread::current && k!=:main
|
192
|
+
end
|
193
|
+
#@logger.run status, 2
|
194
|
+
end
|
195
|
+
@logger.run "\n", 2 unless @fork_mode
|
196
|
+
@logger.run "result: ", 2 unless @fork_mode
|
197
|
+
if flag
|
198
|
+
@logger.run "failure\n", 2 unless @fork_mode
|
199
|
+
else
|
200
|
+
@logger.run "success\n", 2 unless @fork_mode
|
201
|
+
end
|
202
|
+
@failure = flag
|
203
|
+
end
|
204
|
+
def report_collect
|
205
|
+
d = (Time::now - @starttime)*1000
|
206
|
+
if @fork_mode && @forkdb
|
207
|
+
@forkdb.transaction do
|
208
|
+
if not @forkdb['time']
|
209
|
+
@forkdb['time'] = d
|
210
|
+
elsif @forkdb['time'] < d
|
211
|
+
@forkdb['time'] = d
|
212
|
+
end
|
213
|
+
@forkdb['request_count'] = 0 unless @forkdb['request_count']
|
214
|
+
@forkdb['request_count'] += $REQUEST_COUNT
|
215
|
+
end
|
216
|
+
else
|
217
|
+
@logger.run "\nTimePerRequest: #{d / ($REQUEST_COUNT / @config['max_request'])}\n", 2
|
218
|
+
@logger.run "RequestPerSec: #{$REQUEST_COUNT / (d/1000)}\n", 2
|
219
|
+
end
|
220
|
+
record = ResponseRecord::new
|
221
|
+
@threads.each do |k, th|
|
222
|
+
if th[:result]
|
223
|
+
record += th[:result].get_record
|
224
|
+
end
|
225
|
+
end
|
226
|
+
@logger.run("\n\n", 2) unless @fork_mode
|
227
|
+
reporter = BaseHandler::get_handler(@config["reporter"])
|
228
|
+
reporter.run(record)
|
229
|
+
@result_record = record
|
230
|
+
end
|
231
|
+
def run_fork
|
232
|
+
raise "invalid config: fork: nil" unless @config['fork']
|
233
|
+
org_reporter = @config['reporter']
|
234
|
+
|
235
|
+
@logger.run("Process: #{@process_max}\n", 2)
|
236
|
+
@parent = false
|
237
|
+
|
238
|
+
flag = false
|
239
|
+
Signal::trap(:USR1){
|
240
|
+
return if flag
|
241
|
+
flag = true
|
242
|
+
#parent
|
243
|
+
puts "error: SIGUSR1"
|
244
|
+
@pids.each do |pid|
|
245
|
+
Process::kill(:USR2, pid)
|
246
|
+
end
|
247
|
+
}
|
248
|
+
Signal::trap(:INT){
|
249
|
+
flag = true
|
250
|
+
puts "error: SIGINT"
|
251
|
+
@pids.each do |pid|
|
252
|
+
Process::kill(:USR2, pid)
|
253
|
+
end
|
254
|
+
}
|
255
|
+
Signal::trap(:USR2){
|
256
|
+
#child
|
257
|
+
report_collect
|
258
|
+
exit
|
259
|
+
}
|
260
|
+
|
261
|
+
#各プロセスにはpstoreファイルに集計させる
|
262
|
+
@config['reporter'] = 'PStoreReporter'
|
263
|
+
fname = 'tmp.db'
|
264
|
+
fpath = File::join(@basepath, fname)
|
265
|
+
if FileTest::exists?(fpath)
|
266
|
+
raise 'cannot create db' unless FileTest::writable?(fpath)
|
267
|
+
raise 'cannot create db' unless File::delete(fpath)
|
268
|
+
end
|
269
|
+
db = PStore::new(fpath)
|
270
|
+
@forkdb = db
|
271
|
+
|
272
|
+
reporter = BaseHandler::get_handler(@config["reporter"])
|
273
|
+
reporter.set_dir(@basepath)
|
274
|
+
reporter.init_filename(fname)
|
275
|
+
|
276
|
+
begin
|
277
|
+
@pids = []
|
278
|
+
@process = @process_max
|
279
|
+
digit = @process_max.to_s.size
|
280
|
+
tmpl = "%0#{digit}d"
|
281
|
+
1.upto(@process_max) do |i|
|
282
|
+
@pids << Process::fork {
|
283
|
+
@suffix = sprintf(tmpl, i)
|
284
|
+
run_process
|
285
|
+
}
|
286
|
+
#1プロセス目以外は出力を少なく
|
287
|
+
@scenario_handler.silence if i==2
|
288
|
+
end
|
289
|
+
@forkreport = false
|
290
|
+
@org_fork_reporter = org_reporter
|
291
|
+
results = Process::waitall
|
292
|
+
failure = false
|
293
|
+
results.each do |r|
|
294
|
+
if r[1].to_i != 0
|
295
|
+
failure = true
|
296
|
+
break
|
297
|
+
end
|
298
|
+
end
|
299
|
+
@logger.run "\n", 2
|
300
|
+
@logger.run "result: ", 2
|
301
|
+
unless failure
|
302
|
+
@logger.run "success\n", 2
|
303
|
+
else
|
304
|
+
@logger.run "failure\n", 2
|
305
|
+
end
|
306
|
+
fork_report
|
307
|
+
ensure
|
308
|
+
fork_report unless @forkreport
|
309
|
+
File::delete(fpath) if File::exists?(fpath)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
def fork_report
|
313
|
+
@forkreport = true
|
314
|
+
require 'pstore'
|
315
|
+
result = nil
|
316
|
+
time = nil
|
317
|
+
count = nil
|
318
|
+
@forkdb.transaction do
|
319
|
+
result = @forkdb['result']
|
320
|
+
time = @forkdb['time']
|
321
|
+
count = @forkdb['request_count']
|
322
|
+
end
|
323
|
+
@logger.run "\nTimePerRequest: #{time / (count / @config['max_request_show'])}\n", 2 if time && count
|
324
|
+
@logger.run "RequestPerSec: #{count / (time/1000)}\n", 2 if time && count
|
325
|
+
|
326
|
+
reporter = BaseHandler::get_handler(@org_fork_reporter)
|
327
|
+
reporter.run(result)
|
328
|
+
reporter = nil
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
# = ハンドラー
|
3
|
+
# スレッドごと / プロセスごとに存在するマネージャー
|
4
|
+
class BaseHandler
|
5
|
+
#trueだとスレッドごとにインスタンス作成
|
6
|
+
UNIQUE_BY_THREAD = true
|
7
|
+
|
8
|
+
@@instances = nil
|
9
|
+
|
10
|
+
@@config = nil
|
11
|
+
@@scenario_id = nil
|
12
|
+
@@thread_id = nil
|
13
|
+
def self::get_config
|
14
|
+
@@config
|
15
|
+
end
|
16
|
+
|
17
|
+
def self::set_config config
|
18
|
+
@@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
#ゲッター
|
22
|
+
def self::unique_by_thread?
|
23
|
+
self::UNIQUE_BY_THREAD
|
24
|
+
end
|
25
|
+
#スレッドで一意なidを指定します
|
26
|
+
def self::set_thread_id tid
|
27
|
+
Thread::current[:thread_id] = tid
|
28
|
+
end
|
29
|
+
def self::get_thread_id
|
30
|
+
Thread::current[:thread_id]
|
31
|
+
end
|
32
|
+
#クラス名からクラスを取得
|
33
|
+
def self::get_class classname
|
34
|
+
if InternetHakai.const_defined? classname
|
35
|
+
return InternetHakai.const_get(classname)
|
36
|
+
else
|
37
|
+
c = classname.split(/::/).inject(Object) {|c,name|
|
38
|
+
c.const_get(name) if c.const_defined? name
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
#名前からクラス名を取得し、インスタンスを返します
|
43
|
+
def self::get_handler name
|
44
|
+
begin
|
45
|
+
cl = self::get_class(name)
|
46
|
+
rescue
|
47
|
+
raise "invalid class name"
|
48
|
+
end
|
49
|
+
raise "invalid class" unless cl < BaseHandler
|
50
|
+
|
51
|
+
if cl.unique_by_thread?
|
52
|
+
o = cl::get_instance_thread
|
53
|
+
else
|
54
|
+
o = cl::get_instance
|
55
|
+
end
|
56
|
+
o
|
57
|
+
end
|
58
|
+
#スレッドで一意なインスタンスを返します
|
59
|
+
def self::get_instance_thread
|
60
|
+
cth = Thread::current
|
61
|
+
tid = cth[:thread_id]
|
62
|
+
sid = cth[:scenario_id]
|
63
|
+
cth[:handlers] = Hash::new unless cth[:handlers]
|
64
|
+
handlers = cth[:handlers]
|
65
|
+
unless handlers[self.to_s]
|
66
|
+
handlers[self.to_s] = self::new(tid)
|
67
|
+
end
|
68
|
+
handlers[self.to_s]
|
69
|
+
end
|
70
|
+
#スレッドローカルでないインスタンス
|
71
|
+
def self::get_instance
|
72
|
+
@@instances = Hash::new(nil) unless @@instances
|
73
|
+
@@instances[self.to_s] = self::new(nil) unless @@instances[self.to_s]
|
74
|
+
@@instances[self.to_s]
|
75
|
+
end
|
76
|
+
def self::clear
|
77
|
+
@@instances = nil
|
78
|
+
end
|
79
|
+
def self::clearall
|
80
|
+
InternetHakai::constants.each do |c|
|
81
|
+
cl = InternetHakai::const_get(c)
|
82
|
+
if cl.is_a? Class and cl.respond_to? :clear
|
83
|
+
cl.clear
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
#作成時に何かしたい場合はこれをオーバーロード
|
88
|
+
def on_create
|
89
|
+
|
90
|
+
end
|
91
|
+
#コンストラクタ
|
92
|
+
#uid作成などに必要な場合にそなえ、スレッドidを持っている
|
93
|
+
def initialize(th_id)
|
94
|
+
@thread_id = th_id
|
95
|
+
on_create
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# = ベースアクション
|
100
|
+
# アクションはこのクラスを継承して作成
|
101
|
+
class BaseAction
|
102
|
+
#設定ファイルで指定したオプションがわたされる
|
103
|
+
def initialize opt, scenario
|
104
|
+
@opt = get_default.merge(opt)
|
105
|
+
@scenario_handler = BaseHandler::get_handler('ScenarioHandler')
|
106
|
+
@queue = BaseHandler::get_handler(opt['queue'])
|
107
|
+
@method_quit = method(:quit)
|
108
|
+
@scenario = scenario
|
109
|
+
@scenario_id = @scenario.object_id
|
110
|
+
end
|
111
|
+
attr_accessor :performance_id
|
112
|
+
|
113
|
+
# オプションのデフォルト値を指定したい場合はここを上書き
|
114
|
+
def get_default
|
115
|
+
{"times"=>1, "follow_redirect"=>true}
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_on_complete &block
|
119
|
+
@_on_complete_calback = block
|
120
|
+
end
|
121
|
+
def has_on_complete?
|
122
|
+
not not @_on_complete_calback
|
123
|
+
end
|
124
|
+
# コマンドの中身
|
125
|
+
def run
|
126
|
+
@_complete = false
|
127
|
+
#Thread.current[:heartbeat] = Time::now
|
128
|
+
end
|
129
|
+
# デストラクタ
|
130
|
+
def free
|
131
|
+
#puts "free: #{@_complete}: #{object_id}"
|
132
|
+
return if @_complete #1つのリクエストで2回呼ばないように
|
133
|
+
|
134
|
+
@_on_complete_calback.call if @_on_complete_calback
|
135
|
+
#@queue.add(@method_quit)
|
136
|
+
@scenario_handler.quit_action(@scenario_id)
|
137
|
+
@_complete = false
|
138
|
+
end
|
139
|
+
def dispatch_next
|
140
|
+
@_on_complete_calback.call if @_on_complete_calback
|
141
|
+
end
|
142
|
+
def quit
|
143
|
+
@scenario_handler.quit_action(@scenario_id)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|