activemessaging 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +21 -0
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/activemessaging.gemspec +15 -22
- data/lib/activemessaging.rb +3 -7
- data/lib/activemessaging/adapters/amqp.rb +1 -1
- data/lib/activemessaging/adapters/asqs.rb +71 -24
- data/lib/activemessaging/adapters/base.rb +2 -1
- data/lib/activemessaging/adapters/beanstalk.rb +1 -1
- data/lib/activemessaging/adapters/jms.rb +14 -12
- data/lib/activemessaging/adapters/reliable_msg.rb +1 -1
- data/lib/activemessaging/adapters/stomp.rb +1 -1
- data/lib/activemessaging/adapters/test.rb +1 -1
- data/lib/activemessaging/adapters/wmq.rb +1 -1
- data/lib/activemessaging/gateway.rb +47 -44
- data/lib/activemessaging/threaded_poller.rb +191 -0
- data/lib/generators/active_messaging/install/install_generator.rb +6 -1
- data/lib/generators/active_messaging/install/templates/poller.rb +10 -15
- data/lib/generators/active_messaging/install/templates/threaded_poller +45 -0
- metadata +59 -65
- data/README +0 -28
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Active Messaging
|
2
|
+
|
3
|
+
ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc.
|
4
|
+
|
5
|
+
ActiveMessaging is a generic framework to ease using messaging, but is not tied to any particular messaging system - in fact, it now has support for Stomp, AMQP, beanstalk, Amazon Simple Queue Service (SQS), JMS (using StompConnect or direct on JRuby), WebSphere MQ, the all-Ruby ReliableMessaging, a mock 'test' adapter, and a 'synch' adapter for use in development that processes calls synchronously (of course) and so requires no broker or additional processes to be running.
|
6
|
+
|
7
|
+
Here's a sample of a processor class that handles incoming messages:
|
8
|
+
|
9
|
+
class HelloWorldProcessor < ActiveMessaging::Processor
|
10
|
+
subscribes_to :hello_world
|
11
|
+
def on_message(message)
|
12
|
+
puts "received: " + message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# Support
|
18
|
+
|
19
|
+
Best bet is the google groups mailing list:
|
20
|
+
|
21
|
+
http://groups.google.com/group/activemessaging-discuss
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.10.0
|
data/activemessaging.gemspec
CHANGED
@@ -4,19 +4,19 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.
|
7
|
+
s.name = "activemessaging"
|
8
|
+
s.version = "0.10.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jon Tirsen", "Andrew Kuklewicz", "Olle Jonsson", "Sylvain Perez", "Cliff Moon", "Uwe Kubosch"]
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
12
|
+
s.date = "2012-07-13"
|
13
|
+
s.description = "ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc. Now supporting Rails 3 as of version 0.8.0."
|
14
|
+
s.email = "activemessaging-discuss@googlegroups.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
|
-
"README"
|
16
|
+
"README.md"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
|
-
"README",
|
19
|
+
"README.md",
|
20
20
|
"Rakefile",
|
21
21
|
"VERSION",
|
22
22
|
"activemessaging.gemspec",
|
@@ -66,6 +66,7 @@ Gem::Specification.new do |s|
|
|
66
66
|
"lib/activemessaging/processor.rb",
|
67
67
|
"lib/activemessaging/railtie.rb",
|
68
68
|
"lib/activemessaging/test_helper.rb",
|
69
|
+
"lib/activemessaging/threaded_poller.rb",
|
69
70
|
"lib/activemessaging/trace_filter.rb",
|
70
71
|
"lib/generators/active_messaging/install/USAGE",
|
71
72
|
"lib/generators/active_messaging/install/install_generator.rb",
|
@@ -73,6 +74,7 @@ Gem::Specification.new do |s|
|
|
73
74
|
"lib/generators/active_messaging/install/templates/broker.yml",
|
74
75
|
"lib/generators/active_messaging/install/templates/poller",
|
75
76
|
"lib/generators/active_messaging/install/templates/poller.rb",
|
77
|
+
"lib/generators/active_messaging/install/templates/threaded_poller",
|
76
78
|
"lib/generators/active_messaging/processor/USAGE",
|
77
79
|
"lib/generators/active_messaging/processor/processor_generator.rb",
|
78
80
|
"lib/generators/active_messaging/processor/templates/messaging.rb",
|
@@ -93,33 +95,24 @@ Gem::Specification.new do |s|
|
|
93
95
|
"test/test_helper.rb",
|
94
96
|
"test/tracer_test.rb"
|
95
97
|
]
|
96
|
-
s.homepage =
|
98
|
+
s.homepage = "http://github.com/kookster/activemessaging"
|
97
99
|
s.require_paths = ["lib"]
|
98
|
-
s.rubygems_version =
|
99
|
-
s.summary =
|
100
|
-
s.test_files = [
|
101
|
-
"test/all_tests.rb",
|
102
|
-
"test/asqs_test.rb",
|
103
|
-
"test/config_test.rb",
|
104
|
-
"test/filter_test.rb",
|
105
|
-
"test/gateway_test.rb",
|
106
|
-
"test/jms_test.rb",
|
107
|
-
"test/reliable_msg_test.rb",
|
108
|
-
"test/stomp_test.rb",
|
109
|
-
"test/test_helper.rb",
|
110
|
-
"test/tracer_test.rb"
|
111
|
-
]
|
100
|
+
s.rubygems_version = "1.8.22"
|
101
|
+
s.summary = "Official activemessaging gem, now hosted on github.com/kookster. (kookster prefix temporary)"
|
112
102
|
|
113
103
|
if s.respond_to? :specification_version then
|
114
104
|
s.specification_version = 3
|
115
105
|
|
116
106
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
117
107
|
s.add_runtime_dependency(%q<activesupport>, [">= 1.0.0"])
|
108
|
+
s.add_runtime_dependency(%q<celluloid>, [">= 0"])
|
118
109
|
else
|
119
110
|
s.add_dependency(%q<activesupport>, [">= 1.0.0"])
|
111
|
+
s.add_dependency(%q<celluloid>, [">= 0"])
|
120
112
|
end
|
121
113
|
else
|
122
114
|
s.add_dependency(%q<activesupport>, [">= 1.0.0"])
|
115
|
+
s.add_dependency(%q<celluloid>, [">= 0"])
|
123
116
|
end
|
124
117
|
end
|
125
118
|
|
data/lib/activemessaging.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require '
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/all'
|
2
3
|
require 'ostruct'
|
3
4
|
|
4
5
|
if defined?(Rails::Railtie)
|
@@ -45,6 +46,7 @@ module ActiveMessaging
|
|
45
46
|
def self.load_extensions
|
46
47
|
require 'logger'
|
47
48
|
require 'activemessaging/gateway'
|
49
|
+
require 'activemessaging/threaded_poller'
|
48
50
|
require 'activemessaging/adapter'
|
49
51
|
require 'activemessaging/message_sender'
|
50
52
|
require 'activemessaging/processor'
|
@@ -66,8 +68,6 @@ module ActiveMessaging
|
|
66
68
|
path = File.expand_path("#{app_root}/config/messaging.rb")
|
67
69
|
begin
|
68
70
|
load path
|
69
|
-
rescue MissingSourceFile
|
70
|
-
logger.error "ActiveMessaging: no '#{path}' file to load"
|
71
71
|
rescue
|
72
72
|
raise $!, " ActiveMessaging: problems trying to load '#{path}': \n\t#{$!.message}"
|
73
73
|
end
|
@@ -126,7 +126,3 @@ module ActiveMessaging
|
|
126
126
|
end
|
127
127
|
|
128
128
|
end
|
129
|
-
|
130
|
-
if !defined?(Rails::Railtie)
|
131
|
-
ActiveMessaging.load_activemessaging
|
132
|
-
end
|
@@ -79,7 +79,7 @@ module ActiveMessaging
|
|
79
79
|
client.server.send_frame(::Carrot::AMQP::Protocol::Basic::Reject.new(:delivery_tag => message.headers[:delivery_tag]))
|
80
80
|
end
|
81
81
|
|
82
|
-
def receive
|
82
|
+
def receive(options={})
|
83
83
|
while true
|
84
84
|
message = queue.pop(:ack => true)
|
85
85
|
unless message.nil?
|
@@ -17,10 +17,10 @@ module ActiveMessaging
|
|
17
17
|
class Connection < ActiveMessaging::Adapters::BaseConnection
|
18
18
|
register :asqs
|
19
19
|
|
20
|
-
QUEUE_NAME_LENGTH
|
21
|
-
MESSAGE_SIZE
|
22
|
-
VISIBILITY_TIMEOUT
|
23
|
-
NUMBER_OF_MESSAGES
|
20
|
+
QUEUE_NAME_LENGTH = 1..80
|
21
|
+
MESSAGE_SIZE = 1..(8 * 1024)
|
22
|
+
VISIBILITY_TIMEOUT = 0..(24 * 60 * 60)
|
23
|
+
NUMBER_OF_MESSAGES = 1..255
|
24
24
|
GET_QUEUE_ATTRIBUTES = ['All', 'ApproximateNumberOfMessages', 'VisibilityTimeout']
|
25
25
|
SET_QUEUE_ATTRIBUTES = ['VisibilityTimeout']
|
26
26
|
|
@@ -50,6 +50,7 @@ module ActiveMessaging
|
|
50
50
|
|
51
51
|
#initialize the subscriptions and queues
|
52
52
|
@subscriptions = {}
|
53
|
+
@queues_by_priority = {}
|
53
54
|
@current_subscription = 0
|
54
55
|
queues
|
55
56
|
end
|
@@ -69,6 +70,10 @@ module ActiveMessaging
|
|
69
70
|
else
|
70
71
|
@subscriptions[queue.name] = Subscription.new(queue.name, message_headers)
|
71
72
|
end
|
73
|
+
priority = @subscriptions[queue.name].priority
|
74
|
+
|
75
|
+
@queues_by_priority[priority] = [] unless @queues_by_priority.has_key?(priority)
|
76
|
+
@queues_by_priority[priority] << queue.name unless @queues_by_priority[priority].include?(queue.name)
|
72
77
|
end
|
73
78
|
|
74
79
|
# queue_name string, headers hash
|
@@ -76,7 +81,10 @@ module ActiveMessaging
|
|
76
81
|
def unsubscribe queue_name, message_headers={}
|
77
82
|
if @subscriptions[queue_name]
|
78
83
|
@subscriptions[queue_name].remove
|
79
|
-
|
84
|
+
if @subscriptions[queue_name].count <= 0
|
85
|
+
sub = @subscriptions.delete(queue_name)
|
86
|
+
@queues_by_priority[sub.priority].delete(queue_name)
|
87
|
+
end
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
@@ -87,24 +95,60 @@ module ActiveMessaging
|
|
87
95
|
send_messsage queue, message_body
|
88
96
|
end
|
89
97
|
|
90
|
-
# receive
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
98
|
+
# new receive respects priorities
|
99
|
+
def receive(options={})
|
100
|
+
message = nil
|
101
|
+
|
102
|
+
only_priorities = options[:priorities]
|
103
|
+
|
104
|
+
# loop through the priorities
|
105
|
+
@queues_by_priority.keys.sort.each do |priority|
|
106
|
+
|
107
|
+
# skip this priority if there is a list, and it is not in the list
|
108
|
+
next if only_priorities && !only_priorities.include?(priority.to_i)
|
109
|
+
|
110
|
+
# puts " - priority: #{priority}"
|
111
|
+
# loop through queues for the priority in random order each time
|
112
|
+
@queues_by_priority[priority].shuffle.each do |queue_name|
|
113
|
+
# puts " - queue_name: #{queue_name}"
|
114
|
+
queue = queues[queue_name]
|
115
|
+
subscription = @subscriptions[queue_name]
|
116
|
+
|
117
|
+
next if queue.nil? || subscription.nil?
|
118
|
+
messages = retrieve_messsages(queue, 1, subscription.headers[:visibility_timeout])
|
119
|
+
|
120
|
+
if (messages && !messages.empty?)
|
121
|
+
message = messages[0]
|
122
|
+
end
|
123
|
+
|
124
|
+
break if message
|
105
125
|
end
|
126
|
+
|
127
|
+
break if message
|
106
128
|
end
|
107
|
-
|
129
|
+
|
130
|
+
# puts " - message: #{message}"
|
131
|
+
message
|
132
|
+
end
|
133
|
+
|
134
|
+
# # receive a single message from any of the subscribed queues
|
135
|
+
# # check each queue once, then sleep for poll_interval
|
136
|
+
# def receive
|
137
|
+
# raise "No subscriptions to receive messages from." if (@subscriptions.nil? || @subscriptions.empty?)
|
138
|
+
# start = @current_subscription
|
139
|
+
# while true
|
140
|
+
# # puts "calling receive..."
|
141
|
+
# @current_subscription = ((@current_subscription < @subscriptions.length-1) ? @current_subscription + 1 : 0)
|
142
|
+
# sleep poll_interval if (@current_subscription == start)
|
143
|
+
# queue_name = @subscriptions.keys.sort[@current_subscription]
|
144
|
+
# queue = queues[queue_name]
|
145
|
+
# subscription = @subscriptions[queue_name]
|
146
|
+
# unless queue.nil?
|
147
|
+
# messages = retrieve_messsages queue, 1, subscription.headers[:visibility_timeout]
|
148
|
+
# return messages[0] unless (messages.nil? or messages.empty? or messages[0].nil?)
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# end
|
108
152
|
|
109
153
|
def received message, headers={}
|
110
154
|
begin
|
@@ -125,7 +169,7 @@ module ActiveMessaging
|
|
125
169
|
def create_queue(name)
|
126
170
|
validate_new_queue name
|
127
171
|
response = make_request('CreateQueue', nil, {'QueueName'=>name})
|
128
|
-
add_queue
|
172
|
+
add_queue(response.get_text("//QueueUrl")) unless response.nil?
|
129
173
|
end
|
130
174
|
|
131
175
|
def delete_queue queue
|
@@ -264,7 +308,9 @@ module ActiveMessaging
|
|
264
308
|
|
265
309
|
def get_or_create_queue queue_name
|
266
310
|
qs = queues
|
267
|
-
qs.has_key?(queue_name) ? qs[queue_name] : create_queue(queue_name)
|
311
|
+
q = qs.has_key?(queue_name) ? qs[queue_name] : create_queue(queue_name)
|
312
|
+
raise "could not get or create queue: #{queue_name}" unless q
|
313
|
+
q
|
268
314
|
end
|
269
315
|
|
270
316
|
def queues
|
@@ -359,9 +405,10 @@ module ActiveMessaging
|
|
359
405
|
end
|
360
406
|
|
361
407
|
class Subscription
|
362
|
-
attr_accessor :
|
408
|
+
attr_accessor :destination, :headers, :count, :priority
|
363
409
|
|
364
410
|
def initialize(destination, headers={}, count=1)
|
411
|
+
@priority = headers.delete(:priority) || 1001
|
365
412
|
@destination, @headers, @count = destination, headers, count
|
366
413
|
end
|
367
414
|
|
@@ -39,7 +39,8 @@ module ActiveMessaging
|
|
39
39
|
|
40
40
|
# receive a single message from any of the subscribed destinations
|
41
41
|
# check each destination once, then sleep for poll_interval
|
42
|
-
|
42
|
+
# adding options,optionally, so a poller can get certain messages (e.g. by priority)
|
43
|
+
def receive(options={})
|
43
44
|
end
|
44
45
|
|
45
46
|
# called after a message is successfully received and processed
|
@@ -84,22 +84,24 @@ module ActiveMessaging
|
|
84
84
|
end
|
85
85
|
producer.send message
|
86
86
|
end
|
87
|
-
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
87
|
+
|
88
|
+
def receive(options={})
|
89
|
+
queue_name = options[:queue_name]
|
90
|
+
headers = options[:headers] || {}
|
91
|
+
receive_message(queue_name, headers)
|
93
92
|
end
|
94
|
-
|
95
|
-
def
|
93
|
+
|
94
|
+
def receive_message(queue_name=nil, headers={})
|
96
95
|
if queue_name.nil?
|
97
|
-
|
96
|
+
@consumers.find do |k, c|
|
97
|
+
message = c.receive(1)
|
98
|
+
return condition_message(message) unless message.nil?
|
99
|
+
end
|
98
100
|
else
|
99
|
-
consumer = subscribe
|
101
|
+
consumer = subscribe(queue_name, headers)
|
100
102
|
message = consumer.receive(1)
|
101
|
-
unsubscribe
|
102
|
-
condition_message
|
103
|
+
unsubscribe(queue_name, headers)
|
104
|
+
condition_message(message)
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
@@ -89,7 +89,7 @@ module ActiveMessaging
|
|
89
89
|
|
90
90
|
# receive a single message from any of the subscribed destinations
|
91
91
|
# check each destination once, then sleep for poll_interval
|
92
|
-
def receive
|
92
|
+
def receive(options={})
|
93
93
|
|
94
94
|
raise "No subscriptions to receive messages from." if (subscriptions.nil? || subscriptions.empty?)
|
95
95
|
start = current_subscription
|
@@ -73,7 +73,7 @@ module ActiveMessaging
|
|
73
73
|
|
74
74
|
# receive a single message from any of the subscribed destinations
|
75
75
|
# check each destination once, then sleep for poll_interval
|
76
|
-
def receive
|
76
|
+
def receive(options={})
|
77
77
|
m = @stomp_connection.receive
|
78
78
|
Message.new(m) if m
|
79
79
|
end
|
@@ -1,25 +1,28 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'ostruct'
|
3
3
|
require 'erb'
|
4
|
+
require 'thread'
|
4
5
|
|
5
6
|
module ActiveMessaging
|
6
7
|
|
7
8
|
class Gateway
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
|
10
|
+
@adapters = {}
|
11
|
+
@subscriptions = {}
|
12
|
+
@named_destinations = {}
|
13
|
+
@filters = []
|
14
|
+
@connections = {}
|
15
|
+
@processor_groups = {}
|
16
|
+
@current_processor_group = nil
|
16
17
|
|
17
18
|
# these are used to manage the running connection threads
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
class <<self
|
19
|
+
@running = true
|
20
|
+
@connection_threads = {}
|
21
|
+
@guard = Mutex.new
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
attr_accessor :adapters, :subscriptions, :named_destinations, :filters, :processor_groups, :connections
|
23
26
|
|
24
27
|
# Starts up an message listener to start polling for messages on each configured connection, and dispatching processing
|
25
28
|
def start
|
@@ -28,9 +31,9 @@ module ActiveMessaging
|
|
28
31
|
subscribe
|
29
32
|
|
30
33
|
# for each connection, start a thread
|
31
|
-
|
32
|
-
|
33
|
-
while
|
34
|
+
@connections.each do |name, conn|
|
35
|
+
@connection_threads[name] = Thread.start do
|
36
|
+
while @running
|
34
37
|
begin
|
35
38
|
Thread.current[:message] = nil
|
36
39
|
Thread.current[:message] = conn.receive
|
@@ -42,7 +45,11 @@ module ActiveMessaging
|
|
42
45
|
rescue Object=>exception
|
43
46
|
ActiveMessaging.logger.error "ActiveMessaging: thread[#{name}]: Exception from connection.receive: #{exception.message}\n" + exception.backtrace.join("\n\t")
|
44
47
|
ensure
|
45
|
-
|
48
|
+
if Thread.current[:message]
|
49
|
+
@guard.synchronize {
|
50
|
+
dispatch Thread.current[:message]
|
51
|
+
}
|
52
|
+
end
|
46
53
|
Thread.current[:message] = nil
|
47
54
|
end
|
48
55
|
Thread.pass
|
@@ -51,11 +58,11 @@ module ActiveMessaging
|
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
|
-
while
|
61
|
+
while @running
|
55
62
|
trap("TERM", "EXIT")
|
56
63
|
living = false
|
57
|
-
|
58
|
-
|
64
|
+
@connection_threads.each { |name, thread| living ||= thread.alive? }
|
65
|
+
@running = living
|
59
66
|
sleep 1
|
60
67
|
end
|
61
68
|
ActiveMessaging.logger.error "All connection threads have died..."
|
@@ -72,14 +79,14 @@ module ActiveMessaging
|
|
72
79
|
|
73
80
|
def stop
|
74
81
|
# first tell the threads to stop their looping, so they'll stop when next complete a receive/dispatch cycle
|
75
|
-
|
82
|
+
@running = false
|
76
83
|
|
77
84
|
# if they are dispatching (i.e. !thread[:message].nil?), wait for them to finish
|
78
85
|
# if they are receiving (i.e. thread[:message].nil?), stop them by raising exception
|
79
86
|
dispatching = true
|
80
87
|
while dispatching
|
81
88
|
dispatching = false
|
82
|
-
|
89
|
+
@connection_threads.each do |name, thread|
|
83
90
|
if thread[:message]
|
84
91
|
dispatching = true
|
85
92
|
# if thread got killed, but dispatch not done, try it again
|
@@ -109,15 +116,15 @@ module ActiveMessaging
|
|
109
116
|
end
|
110
117
|
|
111
118
|
def connection broker_name='default'
|
112
|
-
return
|
119
|
+
return @connections[broker_name] if @connections.has_key?(broker_name)
|
113
120
|
config = load_connection_configuration(broker_name)
|
114
121
|
adapter_class = Gateway.adapters[config[:adapter]]
|
115
122
|
raise "Unknown messaging adapter #{config[:adapter].inspect}!" if adapter_class.nil?
|
116
|
-
|
123
|
+
@connections[broker_name] = adapter_class.new(config)
|
117
124
|
end
|
118
125
|
|
119
126
|
def register_adapter adapter_name, adapter_class
|
120
|
-
adapters[adapter_name] = adapter_class
|
127
|
+
Gateway.adapters[adapter_name] = adapter_class
|
121
128
|
end
|
122
129
|
|
123
130
|
def filter filter, options = {}
|
@@ -134,8 +141,8 @@ module ActiveMessaging
|
|
134
141
|
end
|
135
142
|
|
136
143
|
def disconnect
|
137
|
-
|
138
|
-
|
144
|
+
@connections.each { |key,connection| connection.disconnect }
|
145
|
+
@connections = {}
|
139
146
|
end
|
140
147
|
|
141
148
|
def execute_filter_chain(direction, message, details={})
|
@@ -199,19 +206,15 @@ module ActiveMessaging
|
|
199
206
|
end
|
200
207
|
|
201
208
|
def dispatch(message)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
ActiveMessaging.logger.flush rescue nil
|
212
|
-
reset_application
|
213
|
-
end
|
214
|
-
}
|
209
|
+
prepare_application
|
210
|
+
_dispatch(message)
|
211
|
+
rescue Object => exc
|
212
|
+
ActiveMessaging.logger.error "Dispatch exception: #{exc}"
|
213
|
+
ActiveMessaging.logger.error exc.backtrace.join("\n\t")
|
214
|
+
raise exc
|
215
|
+
ensure
|
216
|
+
ActiveMessaging.logger.flush rescue nil
|
217
|
+
reset_application
|
215
218
|
end
|
216
219
|
|
217
220
|
def _dispatch(message)
|
@@ -279,7 +282,7 @@ module ActiveMessaging
|
|
279
282
|
proc_name = processor.name.underscore
|
280
283
|
proc_sym = processor.name.underscore.to_sym
|
281
284
|
if (!current_processor_group || processor_groups[current_processor_group].include?(proc_sym))
|
282
|
-
|
285
|
+
@subscriptions["#{proc_name}:#{destination_name}"]= Subscription.new(find_destination(destination_name), processor, headers)
|
283
286
|
end
|
284
287
|
end
|
285
288
|
|
@@ -335,13 +338,13 @@ module ActiveMessaging
|
|
335
338
|
end
|
336
339
|
|
337
340
|
def current_processor_group
|
338
|
-
if ARGV.length > 0 &&
|
341
|
+
if ARGV.length > 0 && !@current_processor_group
|
339
342
|
ARGV.each {|arg|
|
340
343
|
pair = arg.split('=')
|
341
344
|
if pair[0] == 'process-group'
|
342
345
|
group_sym = pair[1].to_sym
|
343
346
|
if processor_groups.has_key? group_sym
|
344
|
-
|
347
|
+
@current_processor_group = group_sym
|
345
348
|
else
|
346
349
|
ActiveMessaging.logger.error "Unrecognized process-group."
|
347
350
|
ActiveMessaging.logger.error "You specified process-group #{pair[1]}, make sure this is specified in config/messaging.rb"
|
@@ -353,7 +356,7 @@ module ActiveMessaging
|
|
353
356
|
end
|
354
357
|
}
|
355
358
|
end
|
356
|
-
|
359
|
+
@current_processor_group
|
357
360
|
end
|
358
361
|
|
359
362
|
def load_connection_configuration(label='default')
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# still working on prioritized worker requests
|
2
|
+
|
3
|
+
|
4
|
+
# This owes no small debt to sidekiq for showing how to use celluloid for polling for messages.
|
5
|
+
# https://github.com/mperham/sidekiq/blob/poller/lib/sidekiq/manager.rb
|
6
|
+
|
7
|
+
require 'celluloid'
|
8
|
+
|
9
|
+
module ActiveMessaging
|
10
|
+
|
11
|
+
class ThreadedPoller
|
12
|
+
|
13
|
+
include Celluloid
|
14
|
+
|
15
|
+
# traps when any worker dies
|
16
|
+
trap_exit :died
|
17
|
+
|
18
|
+
attr_accessor :configuration, :receiver, :connection, :workers, :busy, :running
|
19
|
+
|
20
|
+
#
|
21
|
+
# connection is a string, name of the connection from broker.yml to use for this threaded poller instance
|
22
|
+
#
|
23
|
+
# configuration is a list of hashes
|
24
|
+
# each has describes a group of worker threads
|
25
|
+
# for each group, define what priorities those workers will process
|
26
|
+
# [
|
27
|
+
# {
|
28
|
+
# :pool_size => 1 # number of workers of this type
|
29
|
+
# :priorities => [1,2,3] # what message priorities this thread will process
|
30
|
+
# }
|
31
|
+
# ]
|
32
|
+
#
|
33
|
+
def initialize(connection='default', configuration={})
|
34
|
+
# default config is a pool size of 3 worker threads
|
35
|
+
self.configuration = configuration || [{:pool_size => 3}]
|
36
|
+
self.connection = connection
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
logger.info "ActiveMessaging::ThreadedPoller start"
|
41
|
+
|
42
|
+
# these are workers ready to use
|
43
|
+
self.workers = []
|
44
|
+
|
45
|
+
# these are workers already working
|
46
|
+
self.busy = []
|
47
|
+
|
48
|
+
# this indicates if we are running or not, helps threads to stop gracefully
|
49
|
+
self.running = true
|
50
|
+
|
51
|
+
# subscribe will create the connections based on subscriptions in processsors
|
52
|
+
# (you can't find or use the connection until it is created by calling this)
|
53
|
+
ActiveMessaging::Gateway.subscribe
|
54
|
+
|
55
|
+
# create a message receiver actor, ony need one, using connection
|
56
|
+
receiver_connection = ActiveMessaging::Gateway.connection(connection)
|
57
|
+
self.receiver = MessageReceiver.new(current_actor, receiver_connection)
|
58
|
+
|
59
|
+
# start the workers based on the config
|
60
|
+
configuration.each do |c|
|
61
|
+
(c[:pool_size] || 1).times{ self.workers << Worker.new_link(current_actor, c) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# once all workers are created, start them up
|
65
|
+
self.workers.each{|worker| receive(worker)}
|
66
|
+
|
67
|
+
# in debug level, log info about workers every 10 seconds
|
68
|
+
log_status
|
69
|
+
end
|
70
|
+
|
71
|
+
def stop
|
72
|
+
logger.info "ActiveMessaging::ThreadedPoller stop"
|
73
|
+
# indicates to all busy workers not to pick up another messages, but does not interrupt
|
74
|
+
# also indicates to the message receiver to stop getting more messages
|
75
|
+
self.running = false
|
76
|
+
|
77
|
+
# tell each waiting worker to shut down. Running ones will be allowed to finish
|
78
|
+
workers.each { |w| w.terminate if w.alive? }
|
79
|
+
end
|
80
|
+
|
81
|
+
# recursive method, uses celluloid 'after' to keep calling
|
82
|
+
def log_status
|
83
|
+
return unless logger.debug?
|
84
|
+
logger.debug("ActiveMessaging::ThreadedPoller: conn:#{connection}, #{workers.count}, #{busy.count}, #{running}")
|
85
|
+
after(10){ log_status }
|
86
|
+
end
|
87
|
+
|
88
|
+
def receive(worker)
|
89
|
+
receiver.receive!(worker) if (receiver && running && worker)
|
90
|
+
end
|
91
|
+
|
92
|
+
def dispatch(message, worker)
|
93
|
+
workers.delete(worker)
|
94
|
+
busy << worker
|
95
|
+
worker.execute!(message)
|
96
|
+
end
|
97
|
+
|
98
|
+
def executed(worker)
|
99
|
+
busy.delete(worker)
|
100
|
+
|
101
|
+
if running
|
102
|
+
workers << worker
|
103
|
+
receive(worker)
|
104
|
+
else
|
105
|
+
worker.terminate if worker.alive?
|
106
|
+
if busy.empty?
|
107
|
+
logger.info "all executed: signal stopped"
|
108
|
+
self.terminate if alive?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def died(worker, reason)
|
114
|
+
logger.info "uh oh, #{worker.inspect} died because of #{reason.class}"
|
115
|
+
busy.delete(worker)
|
116
|
+
|
117
|
+
if running
|
118
|
+
worker = Worker.new_link(current_actor)
|
119
|
+
workers << worker
|
120
|
+
receive(worker)
|
121
|
+
else
|
122
|
+
logger.info "check to see if busy is empty: #{busy.inspect}"
|
123
|
+
if busy.empty?
|
124
|
+
logger.info "all died: signal stopped"
|
125
|
+
after(0){ self.terminate } if alive?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def stopped?
|
131
|
+
!alive? || (!running && busy.empty?)
|
132
|
+
end
|
133
|
+
|
134
|
+
def logger; ActiveMessaging.logger; end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class MessageReceiver
|
139
|
+
include Celluloid
|
140
|
+
|
141
|
+
attr_accessor :poller, :connection, :pause
|
142
|
+
|
143
|
+
def initialize(poller, connection, pause=1)
|
144
|
+
logger.debug("MessageReceiver initialize: poller:#{poller}, connection:#{connection}, pause:#{pause}")
|
145
|
+
|
146
|
+
raise "No connection found for '#{poller.connection}'" unless connection
|
147
|
+
|
148
|
+
self.poller = poller
|
149
|
+
self.connection = connection
|
150
|
+
self.pause = pause
|
151
|
+
end
|
152
|
+
|
153
|
+
def receive(worker)
|
154
|
+
return unless poller.running
|
155
|
+
|
156
|
+
message = self.connection.receive(worker.options)
|
157
|
+
|
158
|
+
if message
|
159
|
+
logger.debug("ActiveMessaging::MessageReceiver.receive: message:'#{message.inspect}'")
|
160
|
+
poller.dispatch!(message, worker)
|
161
|
+
else
|
162
|
+
self.terminate if !poller.running && alive?
|
163
|
+
logger.debug("ActiveMessaging::MessageReceiver.receive: no message, retry in #{pause} sec")
|
164
|
+
after(pause) { receive(worker) }
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def logger; ActiveMessaging.logger; end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Worker
|
173
|
+
include Celluloid
|
174
|
+
|
175
|
+
attr_accessor :poller, :options
|
176
|
+
|
177
|
+
def initialize(poller, options)
|
178
|
+
self.poller = poller
|
179
|
+
self.options = options
|
180
|
+
end
|
181
|
+
|
182
|
+
def execute(message)
|
183
|
+
ActiveMessaging::Gateway.dispatch(message)
|
184
|
+
poller.executed!(current_actor)
|
185
|
+
end
|
186
|
+
|
187
|
+
def logger; ActiveMessaging.logger; end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -12,7 +12,12 @@ module ActiveMessaging
|
|
12
12
|
template "poller", "script/#{poller_name}"
|
13
13
|
chmod("script/#{poller_name}", 0755)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
|
+
def copy_threaded_poller
|
17
|
+
template "threaded_poller", "script/threaded_#{poller_name}"
|
18
|
+
chmod("script/threaded_#{poller_name}", 0755)
|
19
|
+
end
|
20
|
+
|
16
21
|
def copy_poller_rb
|
17
22
|
copy_file "poller.rb", "lib/#{poller_name}.rb"
|
18
23
|
end
|
@@ -3,25 +3,20 @@
|
|
3
3
|
STDOUT.sync = true; STDOUT.flush
|
4
4
|
STDERR.sync = true; STDERR.flush
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Merb.root = MERB_ROOT
|
12
|
-
require merb_init_file
|
6
|
+
app_root = ENV['APP_ROOT'] || File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
7
|
+
application_file = File.join(app_root, 'config', 'environment.rb')
|
8
|
+
|
9
|
+
if File.exist?(application_file)
|
10
|
+
load application_file
|
13
11
|
else
|
14
|
-
#
|
15
|
-
RAILS_ROOT=File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
16
|
-
require File.join(RAILS_ROOT, 'config', 'boot')
|
17
|
-
require File.join(RAILS_ROOT, 'config', 'environment')
|
12
|
+
raise "#{application_file} does not exist!"
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
|
-
|
15
|
+
Rails.logger = Logger.new(STDOUT)
|
16
|
+
ActiveMessaging.logger = Rails.logger
|
22
17
|
|
23
|
-
# Load ActiveMessaging
|
24
|
-
|
18
|
+
# Load ActiveMessaging
|
19
|
+
ActiveMessaging::load_processors
|
25
20
|
|
26
21
|
# Start it up!
|
27
22
|
ActiveMessaging::start
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# The threaded poller will use celluloid to generate many threads for a single poller process to handle messages
|
4
|
+
# Yes, I absolutely looked at the sidekiq manager code, and this code is very very similar
|
5
|
+
# This script also shows how to use the poller without loading Rails, though it loads the app lib directory
|
6
|
+
# this is tested on 1.9.3, and rails 3+
|
7
|
+
|
8
|
+
# Make sure stdout and stderr write out without delay for using with daemon like scripts
|
9
|
+
STDOUT.sync = true; STDOUT.flush
|
10
|
+
STDERR.sync = true; STDERR.flush
|
11
|
+
|
12
|
+
# we're not going to load rails, but activemessaging does expect there to be an app root
|
13
|
+
# this goes back to when it was used in both rails or merb, but work with just not loading a framework
|
14
|
+
app_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
15
|
+
ENV['APP_ROOT'] ||= app_root
|
16
|
+
|
17
|
+
# minimal requirements, other requires are in the lib
|
18
|
+
require 'rubygems'
|
19
|
+
require 'bundler/setup'
|
20
|
+
|
21
|
+
# load the app lib directory
|
22
|
+
lib_path = File.dirname("#{app_root}/lib")
|
23
|
+
$:.unshift(lib_path)
|
24
|
+
Dir["#{lib_path}/**/*.rb"].each {|file| require file unless file.match(/poller\.rb/) }
|
25
|
+
|
26
|
+
# require and load activemessaging
|
27
|
+
require 'activemessaging'
|
28
|
+
ActiveMessaging.load_activemessaging
|
29
|
+
|
30
|
+
# configure the connection (there can be multiple defined in broker.yml) and number of threads
|
31
|
+
connection_name = 'default'
|
32
|
+
configuration = [{:pool_size => 3}]
|
33
|
+
|
34
|
+
# start it up!
|
35
|
+
begin
|
36
|
+
trap("TERM", "EXIT")
|
37
|
+
@poller = ActiveMessaging::ThreadedPoller.new(connection_name, configuration)
|
38
|
+
@poller.start!
|
39
|
+
sleep
|
40
|
+
rescue Interrupt
|
41
|
+
puts "-- Interrupt --"
|
42
|
+
@poller.stop!
|
43
|
+
@poller.wait(:shutdown)
|
44
|
+
exit(0)
|
45
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemessaging
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 9
|
9
|
-
- 0
|
10
|
-
version: 0.9.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Jon Tirsen
|
14
9
|
- Andrew Kuklewicz
|
15
10
|
- Olle Jonsson
|
@@ -19,36 +14,52 @@ authors:
|
|
19
14
|
autorequire:
|
20
15
|
bindir: bin
|
21
16
|
cert_chain: []
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
dependencies:
|
26
|
-
- !ruby/object:Gem::Dependency
|
17
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
27
20
|
name: activesupport
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
27
|
+
type: :runtime
|
28
28
|
prerelease: false
|
29
|
-
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
|
-
requirements:
|
32
|
-
- -
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
hash: 23
|
35
|
-
segments:
|
36
|
-
- 1
|
37
|
-
- 0
|
38
|
-
- 0
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
39
34
|
version: 1.0.0
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: celluloid
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
40
43
|
type: :runtime
|
41
|
-
|
42
|
-
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
description: ActiveMessaging is an attempt to bring the simplicity and elegance of
|
52
|
+
rails development to the world of messaging. Messaging, (or event-driven architecture)
|
53
|
+
is widely used for enterprise integration, with frameworks such as Java's JMS, and
|
54
|
+
products such as ActiveMQ, Tibco, IBM MQSeries, etc. Now supporting Rails 3 as of
|
55
|
+
version 0.8.0.
|
43
56
|
email: activemessaging-discuss@googlegroups.com
|
44
57
|
executables: []
|
45
|
-
|
46
58
|
extensions: []
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
- README
|
59
|
+
extra_rdoc_files:
|
60
|
+
- README.md
|
61
|
+
files:
|
62
|
+
- README.md
|
52
63
|
- Rakefile
|
53
64
|
- VERSION
|
54
65
|
- activemessaging.gemspec
|
@@ -98,6 +109,7 @@ files:
|
|
98
109
|
- lib/activemessaging/processor.rb
|
99
110
|
- lib/activemessaging/railtie.rb
|
100
111
|
- lib/activemessaging/test_helper.rb
|
112
|
+
- lib/activemessaging/threaded_poller.rb
|
101
113
|
- lib/activemessaging/trace_filter.rb
|
102
114
|
- lib/generators/active_messaging/install/USAGE
|
103
115
|
- lib/generators/active_messaging/install/install_generator.rb
|
@@ -105,6 +117,7 @@ files:
|
|
105
117
|
- lib/generators/active_messaging/install/templates/broker.yml
|
106
118
|
- lib/generators/active_messaging/install/templates/poller
|
107
119
|
- lib/generators/active_messaging/install/templates/poller.rb
|
120
|
+
- lib/generators/active_messaging/install/templates/threaded_poller
|
108
121
|
- lib/generators/active_messaging/processor/USAGE
|
109
122
|
- lib/generators/active_messaging/processor/processor_generator.rb
|
110
123
|
- lib/generators/active_messaging/processor/templates/messaging.rb
|
@@ -124,48 +137,29 @@ files:
|
|
124
137
|
- test/stomp_test.rb
|
125
138
|
- test/test_helper.rb
|
126
139
|
- test/tracer_test.rb
|
127
|
-
has_rdoc: true
|
128
140
|
homepage: http://github.com/kookster/activemessaging
|
129
141
|
licenses: []
|
130
|
-
|
131
142
|
post_install_message:
|
132
143
|
rdoc_options: []
|
133
|
-
|
134
|
-
require_paths:
|
144
|
+
require_paths:
|
135
145
|
- lib
|
136
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
147
|
none: false
|
138
|
-
requirements:
|
139
|
-
- -
|
140
|
-
- !ruby/object:Gem::Version
|
141
|
-
|
142
|
-
|
143
|
-
- 0
|
144
|
-
version: "0"
|
145
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
153
|
none: false
|
147
|
-
requirements:
|
148
|
-
- -
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
|
151
|
-
segments:
|
152
|
-
- 0
|
153
|
-
version: "0"
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
154
158
|
requirements: []
|
155
|
-
|
156
159
|
rubyforge_project:
|
157
|
-
rubygems_version: 1.
|
160
|
+
rubygems_version: 1.8.22
|
158
161
|
signing_key:
|
159
162
|
specification_version: 3
|
160
|
-
summary: Official activemessaging gem, now hosted on github.com/kookster. (kookster
|
161
|
-
|
162
|
-
|
163
|
-
- test/asqs_test.rb
|
164
|
-
- test/config_test.rb
|
165
|
-
- test/filter_test.rb
|
166
|
-
- test/gateway_test.rb
|
167
|
-
- test/jms_test.rb
|
168
|
-
- test/reliable_msg_test.rb
|
169
|
-
- test/stomp_test.rb
|
170
|
-
- test/test_helper.rb
|
171
|
-
- test/tracer_test.rb
|
163
|
+
summary: Official activemessaging gem, now hosted on github.com/kookster. (kookster
|
164
|
+
prefix temporary)
|
165
|
+
test_files: []
|
data/README
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc.
|
2
|
-
|
3
|
-
ActiveMessaging is a generic framework to ease using messaging, but is not tied to any particular messaging system - in fact, it now has support for Stomp, Amazon Simple Queue Service (SQS), Beanstalk, JMS (using StompConnect or [JMSWithJRuby direct on JRuby]), WebSphere MQ, and the all-Ruby ReliableMessaging.
|
4
|
-
|
5
|
-
Here's a sample of a processor class that handles incoming messages:
|
6
|
-
|
7
|
-
class HelloWorldProcessor < ActiveMessaging::Processor
|
8
|
-
|
9
|
-
subscribes_to :hello_world
|
10
|
-
|
11
|
-
def on_message(message)
|
12
|
-
puts "received: " + message
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
h1. Support
|
19
|
-
|
20
|
-
Best bet is the google groups mailing list:
|
21
|
-
|
22
|
-
http://groups.google.com/group/activemessaging-discuss
|
23
|
-
|
24
|
-
h1. Fork Details
|
25
|
-
|
26
|
-
This fork adds the ability to specify a dead letter queue prefix so instead of all messages going to 1 dead letter queue, each message will go to a dead letter queue that is based on the deadLetterQueuePrefix configuration property and the originating queue.
|
27
|
-
|
28
|
-
For example, if you specific a deadLetterQueuePrefix of "DLQ." in your broker.yml and the originating queue is /queue/myqueue then the dead letter queue will be /queue/DLQ.myqueue
|