modern_times 0.2.11 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +114 -80
- data/VERSION +1 -1
- data/examples/advanced_requestor/README +15 -0
- data/examples/advanced_requestor/base_request_worker.rb +13 -0
- data/examples/advanced_requestor/char_count_worker.rb +11 -0
- data/examples/advanced_requestor/exception_raiser_worker.rb +10 -0
- data/examples/advanced_requestor/length_worker.rb +9 -0
- data/examples/advanced_requestor/manager.rb +22 -0
- data/examples/advanced_requestor/modern_times.yml +32 -0
- data/examples/advanced_requestor/print_worker.rb +9 -0
- data/examples/advanced_requestor/publish.rb +46 -0
- data/examples/advanced_requestor/reverse_worker.rb +9 -0
- data/examples/advanced_requestor/triple_worker.rb +9 -0
- data/examples/requestor/request.rb +3 -3
- data/examples/requestor/reverse_echo_worker.rb +1 -2
- data/lib/modern_times.rb +1 -1
- data/lib/modern_times/base/supervisor.rb +2 -0
- data/lib/modern_times/base/worker.rb +5 -3
- data/lib/modern_times/jms.rb +2 -0
- data/lib/modern_times/jms/connection.rb +7 -0
- data/lib/modern_times/jms/publish_handle.rb +219 -0
- data/lib/modern_times/jms/publisher.rb +55 -29
- data/lib/modern_times/{jms_requestor/worker.rb → jms/request_worker.rb} +29 -51
- data/lib/modern_times/jms/supervisor.rb +30 -0
- data/lib/modern_times/jms/supervisor_mbean.rb +17 -1
- data/lib/modern_times/jms/worker.rb +43 -40
- data/lib/modern_times/manager.rb +6 -2
- data/lib/modern_times/marshal_strategy.rb +14 -17
- data/lib/modern_times/marshal_strategy/bson.rb +2 -0
- data/lib/modern_times/marshal_strategy/json.rb +3 -0
- data/lib/modern_times/marshal_strategy/ruby.rb +3 -0
- data/lib/modern_times/marshal_strategy/string.rb +3 -0
- data/lib/modern_times/marshal_strategy/yaml.rb +3 -0
- data/lib/modern_times/railsable.rb +7 -14
- data/lib/modern_times/time_track.rb +84 -0
- data/test/jms.yml +1 -0
- data/test/jms_failure_test.rb +128 -0
- data/test/jms_requestor_block_test.rb +275 -0
- data/test/jms_requestor_test.rb +71 -96
- data/test/jms_test.rb +59 -78
- data/test/marshal_strategy_test.rb +1 -3
- metadata +29 -14
- data/examples/exception_test/bar_worker.rb +0 -8
- data/examples/exception_test/base_worker.rb +0 -23
- data/examples/exception_test/manager.rb +0 -11
- data/lib/modern_times/jms_requestor.rb +0 -10
- data/lib/modern_times/jms_requestor/request_handle.rb +0 -42
- data/lib/modern_times/jms_requestor/requestor.rb +0 -56
- data/lib/modern_times/jms_requestor/supervisor.rb +0 -45
- data/lib/modern_times/jms_requestor/supervisor_mbean.rb +0 -21
@@ -1,88 +1,66 @@
|
|
1
1
|
module ModernTimes
|
2
|
-
module
|
2
|
+
module JMS
|
3
3
|
|
4
4
|
# Base Worker Class for any class that will be processing messages from queues
|
5
|
-
module
|
6
|
-
include
|
7
|
-
|
5
|
+
module RequestWorker
|
6
|
+
include Worker
|
7
|
+
# Dummy requesting needs access to this
|
8
|
+
attr_reader :marshaler
|
8
9
|
|
9
10
|
module ClassMethods
|
10
|
-
|
11
|
-
|
11
|
+
# Define the marshaling and time_to_live that will occur on the response
|
12
|
+
def response(options)
|
13
|
+
@response_options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def response_options
|
17
|
+
# Get the response marshaler, defaulting to the request marshaler
|
18
|
+
@response_options
|
12
19
|
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def self.included(base)
|
16
23
|
# The price we pay for including rather than extending
|
17
24
|
base.extend(ModernTimes::Base::Worker::ClassMethods)
|
18
|
-
base.extend(
|
25
|
+
base.extend(Worker::ClassMethods)
|
19
26
|
base.extend(ClassMethods)
|
20
27
|
end
|
21
28
|
|
22
29
|
def initialize(opts={})
|
23
30
|
super
|
24
|
-
|
25
|
-
@
|
26
|
-
@
|
27
|
-
|
28
|
-
@
|
29
|
-
@total_time = 0.0
|
31
|
+
response_options = self.class.response_options || {}
|
32
|
+
@marshal_type = (response_options[:marshal] || :ruby).to_s
|
33
|
+
@marshaler = MarshalStrategy.find(@marshal_type)
|
34
|
+
# Time in msec until the message gets discarded, should be more than the timeout on the requestor side
|
35
|
+
@time_to_live = response_options[:time_to_live] || 10000
|
30
36
|
end
|
31
37
|
|
32
38
|
def perform(object)
|
33
|
-
start_time = Time.now
|
34
39
|
response = request(object)
|
35
|
-
response_time = Time.now - start_time
|
36
40
|
session.producer(:destination => message.reply_to) do |producer|
|
37
|
-
|
41
|
+
producer.time_to_live = @time_to_live
|
42
|
+
reply_message = ModernTimes::JMS.create_message(session, @marshaler, response)
|
38
43
|
reply_message.jms_correlation_id = message.jms_message_id
|
44
|
+
reply_message.jms_delivery_mode = ::JMS::DeliveryMode::NON_PERSISTENT
|
45
|
+
reply_message['worker'] = self.name
|
46
|
+
reply_message['marshal'] = @marshal_type
|
39
47
|
producer.send(reply_message)
|
40
48
|
end
|
41
|
-
@time_mutex.synchronize do
|
42
|
-
@count += 1
|
43
|
-
@total_time += response_time
|
44
|
-
@min_time = response_time if !@min_time || response_time < @min_time
|
45
|
-
@max_time = response_time if response_time > @max_time
|
46
|
-
end
|
47
49
|
rescue Exception => e
|
48
|
-
|
49
|
-
@error_count += 1
|
50
|
-
end
|
50
|
+
ModernTimes.logger.error("Exception: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
51
51
|
begin
|
52
52
|
session.producer(:destination => message.reply_to) do |producer|
|
53
|
+
producer.time_to_live = @time_to_live
|
53
54
|
reply_message = ModernTimes::JMS.create_message(session, ModernTimes::MarshalStrategy::String, "Exception: #{e.message}")
|
54
55
|
reply_message.jms_correlation_id = message.jms_message_id
|
55
|
-
reply_message['
|
56
|
+
reply_message['worker'] = self.name
|
57
|
+
reply_message['exception'] = ModernTimes::RemoteException.new(e).to_hash.to_yaml
|
56
58
|
producer.send(reply_message)
|
57
59
|
end
|
58
60
|
rescue Exception => e
|
59
61
|
ModernTimes.logger.error("Exception in exception reply: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
60
62
|
end
|
61
|
-
|
62
|
-
|
63
|
-
def total_time
|
64
|
-
@time_mutex.synchronize do
|
65
|
-
retval = [@count, @total_time]
|
66
|
-
@count = 0
|
67
|
-
@total_time = 0.0
|
68
|
-
return retval
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def min_time
|
73
|
-
@time_mutex.synchronize do
|
74
|
-
val = @min_time
|
75
|
-
@min_time = nil
|
76
|
-
return val
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def max_time
|
81
|
-
@time_mutex.synchronize do
|
82
|
-
val = @max_time
|
83
|
-
@max_time = 0.0
|
84
|
-
return val
|
85
|
-
end
|
63
|
+
raise
|
86
64
|
end
|
87
65
|
|
88
66
|
def request(object)
|
@@ -10,6 +10,36 @@ module ModernTimes
|
|
10
10
|
workers.map { |w| w.message_count }
|
11
11
|
end
|
12
12
|
|
13
|
+
def average_response_time
|
14
|
+
count = 0
|
15
|
+
total = 0.0
|
16
|
+
workers.each do |w|
|
17
|
+
pair = w.time_track.total_time_reset
|
18
|
+
count += pair.first
|
19
|
+
total += pair.last
|
20
|
+
end
|
21
|
+
return 0.0 if count == 0
|
22
|
+
return total / count
|
23
|
+
end
|
24
|
+
|
25
|
+
def min_response_time
|
26
|
+
min_time = nil
|
27
|
+
workers.each do |w|
|
28
|
+
wmin_time = w.time_track.min_time_reset
|
29
|
+
min_time = wmin_time if wmin_time && (!min_time || wmin_time < min_time)
|
30
|
+
end
|
31
|
+
return min_time || 0.0
|
32
|
+
end
|
33
|
+
|
34
|
+
def max_response_time
|
35
|
+
max_time = 0.0
|
36
|
+
workers.each do |w|
|
37
|
+
wmax_time = w.time_track.max_time_reset
|
38
|
+
max_time = wmax_time if wmax_time > max_time
|
39
|
+
end
|
40
|
+
return max_time
|
41
|
+
end
|
42
|
+
|
13
43
|
# Make JMS::SupervisorMBean our mbean
|
14
44
|
def create_mbean(domain)
|
15
45
|
SupervisorMBean.new(mbean_name(domain), mbean_description, self, {})
|
@@ -1,11 +1,27 @@
|
|
1
1
|
module ModernTimes
|
2
2
|
module JMS
|
3
3
|
class SupervisorMBean < ModernTimes::Base::SupervisorMBean
|
4
|
-
r_attribute :message_counts,
|
4
|
+
r_attribute :message_counts, :list, 'Message counts for the workers', :message_counts
|
5
|
+
r_attribute :average_response_time, :float, 'Average response time', :average_response_time
|
6
|
+
r_attribute :min_response_time, :float, 'Minimum response time', :min_response_time
|
7
|
+
r_attribute :max_response_time, :float, 'Maximum response time', :max_response_time
|
5
8
|
|
6
9
|
def message_counts
|
7
10
|
java.util.ArrayList.new(supervisor.message_counts)
|
8
11
|
end
|
12
|
+
|
13
|
+
def average_response_time
|
14
|
+
supervisor.average_response_time
|
15
|
+
end
|
16
|
+
|
17
|
+
def min_response_time
|
18
|
+
supervisor.min_response_time
|
19
|
+
end
|
20
|
+
|
21
|
+
def max_response_time
|
22
|
+
supervisor.max_response_time
|
23
|
+
end
|
24
|
+
|
9
25
|
end
|
10
26
|
end
|
11
27
|
end
|
@@ -34,7 +34,7 @@ module ModernTimes
|
|
34
34
|
module Worker
|
35
35
|
include ModernTimes::Base::Worker
|
36
36
|
|
37
|
-
attr_reader :session, :
|
37
|
+
attr_reader :session, :error_count, :time_track
|
38
38
|
attr_accessor :message
|
39
39
|
|
40
40
|
module ClassMethods
|
@@ -42,10 +42,6 @@ module ModernTimes
|
|
42
42
|
Supervisor.new(manager, self, {}, worker_options)
|
43
43
|
end
|
44
44
|
|
45
|
-
def marshal(option)
|
46
|
-
@marshaler = ModernTimes::MarshalStrategy.find(option)
|
47
|
-
end
|
48
|
-
|
49
45
|
def destination_options
|
50
46
|
options = dest_options.dup
|
51
47
|
# Default the queue name to the Worker name if a destinations hasn't been specified
|
@@ -64,13 +60,18 @@ module ModernTimes
|
|
64
60
|
dest_options[:queue_name] = name.to_s
|
65
61
|
end
|
66
62
|
|
67
|
-
|
63
|
+
def dest_options
|
68
64
|
@dest_options ||= {}
|
69
65
|
end
|
70
66
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
67
|
+
def failure_queue(name, opts={})
|
68
|
+
@failure_queue_name = name.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def failure_queue_name
|
72
|
+
# Don't overwrite if the user set to false, only if it was never set
|
73
|
+
@failure_queue_name = "#{default_name}Failure" if @failure_queue_name.nil?
|
74
|
+
@failure_queue_name
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
@@ -81,15 +82,18 @@ module ModernTimes
|
|
81
82
|
|
82
83
|
def initialize(opts={})
|
83
84
|
super
|
84
|
-
@status
|
85
|
-
|
85
|
+
@status = 'initialized'
|
86
|
+
# Supervisor will ask for counts in a separate thread
|
87
|
+
@time_track = ModernTimes::TimeTrack.new
|
88
|
+
@error_count = 0
|
86
89
|
end
|
87
90
|
|
88
|
-
def
|
91
|
+
def message_count
|
92
|
+
@time_track.total_count
|
89
93
|
end
|
90
94
|
|
91
95
|
def status
|
92
|
-
@status || "Processing message #{
|
96
|
+
@status || "Processing message #{@time_track.total_count}"
|
93
97
|
end
|
94
98
|
|
95
99
|
def real_destination_options
|
@@ -101,8 +105,6 @@ module ModernTimes
|
|
101
105
|
|
102
106
|
# Start the event loop for handling messages off the queue
|
103
107
|
def start
|
104
|
-
# Grab this to prevent lookup with every message
|
105
|
-
@message_marshaler = self.class.marshaler
|
106
108
|
@session = Connection.create_consumer_session
|
107
109
|
@consumer = @session.consumer(real_destination_options)
|
108
110
|
@session.start
|
@@ -110,24 +112,23 @@ module ModernTimes
|
|
110
112
|
ModernTimes.logger.debug "#{self}: Starting receive loop"
|
111
113
|
@status = nil
|
112
114
|
while msg = @consumer.receive
|
113
|
-
|
114
|
-
@message_count += 1
|
115
|
+
@time_track.perform do
|
115
116
|
on_message(msg)
|
116
117
|
msg.acknowledge
|
117
118
|
end
|
118
|
-
ModernTimes.logger.info {"#{self}::
|
119
|
+
ModernTimes.logger.info {"#{self}::on_message (#{('%.1f' % @time_track.last_time)}ms)"} if ModernTimes::JMS::Connection.log_times?
|
119
120
|
end
|
120
121
|
@status = 'Exited'
|
121
122
|
ModernTimes.logger.info "#{self}: Exiting"
|
122
|
-
rescue javax.jms.IllegalStateException => e
|
123
|
-
#if e.cause.code == Java::org.jms.api.core.JMSException::OBJECT_CLOSED
|
124
|
-
# Normal exit
|
125
|
-
@status = 'Exited'
|
126
|
-
ModernTimes.logger.info "#{self}: Exiting due to close"
|
127
|
-
#else
|
128
|
-
# @status = "Exited with JMS exception #{e.message}"
|
129
|
-
# ModernTImes.logger.error "#{self} JMSException: #{e.message}\n#{e.backtrace.join("\n")}"
|
130
|
-
#end
|
123
|
+
# rescue javax.jms.IllegalStateException => e
|
124
|
+
# #if e.cause.code == Java::org.jms.api.core.JMSException::OBJECT_CLOSED
|
125
|
+
# # Normal exit
|
126
|
+
# @status = 'Exited'
|
127
|
+
# ModernTimes.logger.info "#{self}: Exiting due to possible close: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
128
|
+
# #else
|
129
|
+
# # @status = "Exited with JMS exception #{e.message}"
|
130
|
+
# # ModernTImes.logger.error "#{self} JMSException: #{e.message}\n#{e.backtrace.join("\n")}"
|
131
|
+
# #end
|
131
132
|
rescue Exception => e
|
132
133
|
@status = "Exited with exception #{e.message}"
|
133
134
|
ModernTimes.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
@@ -143,31 +144,33 @@ module ModernTimes
|
|
143
144
|
|
144
145
|
def on_message(message)
|
145
146
|
@message = message
|
146
|
-
|
147
|
+
marshaler = ModernTimes::MarshalStrategy.find(message['marshal'] || :ruby)
|
148
|
+
object = marshaler.unmarshal(message.data)
|
147
149
|
ModernTimes.logger.debug {"#{self}: Received Object: #{object}"}
|
148
150
|
perform(object)
|
151
|
+
rescue Exception => e
|
152
|
+
@error_count += 1
|
153
|
+
on_exception(e)
|
154
|
+
ensure
|
149
155
|
ModernTimes.logger.debug {"#{self}: Finished processing message"}
|
150
156
|
ModernTimes.logger.flush if ModernTimes.logger.respond_to?(:flush)
|
151
|
-
rescue Exception => e
|
152
|
-
ModernTimes.logger.error "#{self}: Messaging Exception: #{e.inspect}\n#{e.backtrace.inspect}"
|
153
|
-
rescue java.lang.Exception => e
|
154
|
-
ModernTimes.logger.error "#{self}: Java Messaging Exception: #{e.inspect}\n#{e.backtrace.inspect}"
|
155
157
|
end
|
156
158
|
|
157
159
|
def perform(object)
|
158
160
|
raise "#{self}: Need to override perform method in #{self.class.name} in order to act on #{object}"
|
159
161
|
end
|
160
162
|
|
161
|
-
def
|
162
|
-
"#{
|
163
|
+
def on_exception(e)
|
164
|
+
ModernTimes.logger.error "#{self}: Messaging Exception: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
165
|
+
if self.class.failure_queue_name
|
166
|
+
session.producer(:queue_name => self.class.failure_queue_name) do |producer|
|
167
|
+
producer.send(message)
|
168
|
+
end
|
169
|
+
end
|
163
170
|
end
|
164
171
|
|
165
|
-
|
166
|
-
|
167
|
-
#########
|
168
|
-
|
169
|
-
# Create session information and allow extenders to initialize anything necessary prior to the event loop
|
170
|
-
def session_init
|
172
|
+
def to_s
|
173
|
+
"#{real_destination_options.to_a.join('=>')}:#{index}"
|
171
174
|
end
|
172
175
|
end
|
173
176
|
end
|
data/lib/modern_times/manager.rb
CHANGED
@@ -11,11 +11,11 @@ module ModernTimes
|
|
11
11
|
@config = config
|
12
12
|
@domain = config[:domain] || ModernTimes::DEFAULT_DOMAIN
|
13
13
|
@supervisors = []
|
14
|
+
@dummy_host = config[:dummy_host]
|
14
15
|
self.persist_file = config[:persist_file]
|
15
16
|
self.worker_file = config[:worker_file]
|
16
17
|
@allowed_workers = config[:allowed_workers]
|
17
18
|
stop_on_signal if config[:stop_on_signal]
|
18
|
-
@dummy_host = config[:dummy_host]
|
19
19
|
# Unless specifically unconfigured (i.e., Rails.env == test), then enable jmx
|
20
20
|
if config[:jmx] != false
|
21
21
|
@jmx_server = JMX::MBeanServer.new
|
@@ -60,6 +60,7 @@ module ModernTimes
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def stop
|
63
|
+
return if @stopped
|
63
64
|
@stopped = true
|
64
65
|
@supervisors.each { |supervisor| supervisor.stop }
|
65
66
|
end
|
@@ -81,8 +82,9 @@ module ModernTimes
|
|
81
82
|
end
|
82
83
|
|
83
84
|
def persist_file=(file)
|
85
|
+
return if @persist_file == file
|
86
|
+
@persist_file = nil
|
84
87
|
return unless file
|
85
|
-
@persist_file = file
|
86
88
|
if File.exist?(file)
|
87
89
|
hash = YAML.load_file(file)
|
88
90
|
hash.each do |worker_name, worker_hash|
|
@@ -93,6 +95,7 @@ module ModernTimes
|
|
93
95
|
add(klass, count, options)
|
94
96
|
end
|
95
97
|
end
|
98
|
+
@persist_file = file
|
96
99
|
end
|
97
100
|
|
98
101
|
def save_persist_state
|
@@ -107,6 +110,7 @@ module ModernTimes
|
|
107
110
|
end
|
108
111
|
File.open(@persist_file, 'w') do |out|
|
109
112
|
YAML.dump(hash, out )
|
113
|
+
YAML.dump(hash, out )
|
110
114
|
end
|
111
115
|
end
|
112
116
|
|
@@ -1,9 +1,3 @@
|
|
1
|
-
require 'modern_times/marshal_strategy/bson'
|
2
|
-
require 'modern_times/marshal_strategy/json'
|
3
|
-
require 'modern_times/marshal_strategy/ruby'
|
4
|
-
require 'modern_times/marshal_strategy/string'
|
5
|
-
require 'modern_times/marshal_strategy/yaml'
|
6
|
-
|
7
1
|
# Defines some default marshaling strategies for use in marshaling/unmarshaling objects
|
8
2
|
# written and read via jms. Implementing classes must define the following methods:
|
9
3
|
#
|
@@ -27,26 +21,23 @@ require 'modern_times/marshal_strategy/yaml'
|
|
27
21
|
|
28
22
|
module ModernTimes
|
29
23
|
module MarshalStrategy
|
30
|
-
|
31
|
-
|
32
|
-
:string => String,
|
33
|
-
:json => JSON,
|
34
|
-
:bson => BSON,
|
35
|
-
:yaml => YAML,
|
36
|
-
}
|
24
|
+
|
25
|
+
@options = {}
|
37
26
|
|
38
27
|
def self.find(marshaler)
|
39
28
|
if marshaler.nil?
|
40
29
|
return Ruby
|
41
|
-
|
42
|
-
val = @options[marshaler]
|
30
|
+
else
|
31
|
+
val = @options[marshaler.to_sym]
|
43
32
|
return val if val
|
44
|
-
elsif valid?(marshaler)
|
45
|
-
return marshaler
|
46
33
|
end
|
47
34
|
raise "Invalid marshal strategy: #{marshaler}"
|
48
35
|
end
|
49
36
|
|
37
|
+
def self.registered?(marshaler)
|
38
|
+
@options.has_key?(marshaler.to_sym)
|
39
|
+
end
|
40
|
+
|
50
41
|
# Allow user-defined marshal strategies
|
51
42
|
def self.register(hash)
|
52
43
|
hash.each do |key, marshaler|
|
@@ -66,3 +57,9 @@ module ModernTimes
|
|
66
57
|
end
|
67
58
|
end
|
68
59
|
end
|
60
|
+
|
61
|
+
require 'modern_times/marshal_strategy/bson'
|
62
|
+
require 'modern_times/marshal_strategy/json'
|
63
|
+
require 'modern_times/marshal_strategy/ruby'
|
64
|
+
require 'modern_times/marshal_strategy/string'
|
65
|
+
require 'modern_times/marshal_strategy/yaml'
|