modern_times 0.2.11 → 0.3.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/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'
|