internethakai 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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