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.
Files changed (36) hide show
  1. data/CHANGES +53 -0
  2. data/README +177 -0
  3. data/bin/hakaigen +10 -0
  4. data/bin/internethakai +9 -0
  5. data/internethakai.gemspec +27 -0
  6. data/lib/internethakai.rb +26 -0
  7. data/lib/internethakai.rb.~1~ +25 -0
  8. data/lib/internethakai/action.rb +403 -0
  9. data/lib/internethakai/client_handler.rb +175 -0
  10. data/lib/internethakai/client_queue.rb +27 -0
  11. data/lib/internethakai/concurrency_manager.rb +333 -0
  12. data/lib/internethakai/concurrency_manager.rb.~1~ +331 -0
  13. data/lib/internethakai/core.rb +146 -0
  14. data/lib/internethakai/executor.rb +129 -0
  15. data/lib/internethakai/generator.rb +32 -0
  16. data/lib/internethakai/hakairev.rb +119 -0
  17. data/lib/internethakai/hakairev/executor.rb +43 -0
  18. data/lib/internethakai/hakairev/http_client.rb +362 -0
  19. data/lib/internethakai/hakairev/http_client.rb.~1~ +371 -0
  20. data/lib/internethakai/hakairev/monkey.rb +70 -0
  21. data/lib/internethakai/hakairev/revpipe.rb +39 -0
  22. data/lib/internethakai/hakairev/task.rb +39 -0
  23. data/lib/internethakai/hakairev/time_register.rb +116 -0
  24. data/lib/internethakai/hakairev/timer_factory.rb +38 -0
  25. data/lib/internethakai/http_client.rb +120 -0
  26. data/lib/internethakai/logger.rb +52 -0
  27. data/lib/internethakai/main.rb +90 -0
  28. data/lib/internethakai/reporter.rb +143 -0
  29. data/lib/internethakai/response.rb +65 -0
  30. data/lib/internethakai/scenario.rb +98 -0
  31. data/lib/internethakai/scenario.tmpl +58 -0
  32. data/lib/internethakai/scenario_builder.rb +183 -0
  33. data/lib/internethakai/scenario_handler.rb +91 -0
  34. data/lib/internethakai/time_register.rb +53 -0
  35. data/lib/internethakai/util.rb +130 -0
  36. 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