hot_bunnies 2.0.0.pre9-java → 2.0.0.pre10-java
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/lib/ext/rabbitmq-client.jar +0 -0
- data/lib/hot_bunnies/channel.rb +138 -11
- data/lib/hot_bunnies/consumers/base.rb +16 -3
- data/lib/hot_bunnies/consumers/blocking.rb +2 -2
- data/lib/hot_bunnies/consumers/non_blocking.rb +29 -9
- data/lib/hot_bunnies/exchange.rb +6 -0
- data/lib/hot_bunnies/juc.rb +1 -0
- data/lib/hot_bunnies/queue.rb +153 -8
- data/lib/hot_bunnies/session.rb +64 -3
- data/lib/hot_bunnies/version.rb +1 -1
- metadata +2 -2
data/lib/ext/rabbitmq-client.jar
CHANGED
Binary file
|
data/lib/hot_bunnies/channel.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "hot_bunnies/shutdown_listener"
|
3
|
+
require "hot_bunnies/juc"
|
3
4
|
|
4
5
|
module HotBunnies
|
5
6
|
# ## Channels in RabbitMQ
|
@@ -121,10 +122,14 @@ module HotBunnies
|
|
121
122
|
@connection = session
|
122
123
|
@delegate = delegate
|
123
124
|
|
124
|
-
|
125
|
+
@exchanges = ConcurrentHashMap.new
|
126
|
+
@queues = ConcurrentHashMap.new
|
127
|
+
# we keep track of consumers in part to gracefully shut down their
|
125
128
|
# executors when the channel is closed. This frees library users
|
126
129
|
# from having to worry about this. MK.
|
127
|
-
@consumers
|
130
|
+
@consumers = ConcurrentHashMap.new
|
131
|
+
@shutdown_hooks = ConcurrentSkipListSet.new
|
132
|
+
@recoveries_counter = JavaConcurrent::AtomicInteger.new(0)
|
128
133
|
|
129
134
|
on_shutdown do |ch, cause|
|
130
135
|
ch.gracefully_shut_down_consumers
|
@@ -180,11 +185,84 @@ module HotBunnies
|
|
180
185
|
|
181
186
|
def on_shutdown(&block)
|
182
187
|
sh = ShutdownListener.new(self, &block)
|
183
|
-
|
188
|
+
|
189
|
+
@shutdown_hooks << sh
|
190
|
+
@delegate.add_shutdown_listener(sh)
|
184
191
|
|
185
192
|
sh
|
186
193
|
end
|
187
194
|
|
195
|
+
# @private
|
196
|
+
def automatically_recover(session, java_connection)
|
197
|
+
jch = java_connection.create_channel(id)
|
198
|
+
|
199
|
+
self.revive_with(jch)
|
200
|
+
self.recover_shutdown_hooks
|
201
|
+
|
202
|
+
self.recover_prefetch_setting
|
203
|
+
self.recover_exchanges
|
204
|
+
# this includes recovering bindings
|
205
|
+
self.recover_queues
|
206
|
+
self.recover_consumers
|
207
|
+
self.increment_recoveries_counter
|
208
|
+
end
|
209
|
+
|
210
|
+
# @private
|
211
|
+
def revive_with(java_ch)
|
212
|
+
@delegate = java_ch
|
213
|
+
end
|
214
|
+
|
215
|
+
# @private
|
216
|
+
def recover_shutdown_hooks
|
217
|
+
@shutdown_hooks.each do |sh|
|
218
|
+
@delegate.add_shutdown_listener(sh)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Recovers basic.qos setting. Used by the Automatic Network Failure
|
223
|
+
# Recovery feature.
|
224
|
+
#
|
225
|
+
# @api plugin
|
226
|
+
def recover_prefetch_setting
|
227
|
+
basic_qos(@prefetch_count) if @prefetch_count
|
228
|
+
end
|
229
|
+
|
230
|
+
# Recovers exchanges. Used by the Automatic Network Failure
|
231
|
+
# Recovery feature.
|
232
|
+
#
|
233
|
+
# @api plugin
|
234
|
+
def recover_exchanges
|
235
|
+
@exchanges.values.each do |x|
|
236
|
+
x.recover_from_network_failure
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Recovers queues and bindings. Used by the Automatic Network Failure
|
241
|
+
# Recovery feature.
|
242
|
+
#
|
243
|
+
# @api private
|
244
|
+
def recover_queues
|
245
|
+
@queues.values.each do |q|
|
246
|
+
q.recover_from_network_failure
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Recovers consumers. Used by the Automatic Network Failure
|
251
|
+
# Recovery feature.
|
252
|
+
#
|
253
|
+
# @api private
|
254
|
+
def recover_consumers
|
255
|
+
@consumers.values.each do |c|
|
256
|
+
self.unregister_consumer(c)
|
257
|
+
c.recover_from_network_failure
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# @private
|
262
|
+
def increment_recoveries_counter
|
263
|
+
@recoveries_counter.increment
|
264
|
+
end
|
265
|
+
|
188
266
|
# @group Exchanges
|
189
267
|
|
190
268
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -202,9 +280,11 @@ module HotBunnies
|
|
202
280
|
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
203
281
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
204
282
|
def exchange(name, options={})
|
205
|
-
Exchange.new(self, name, options).tap do |x|
|
283
|
+
dx = Exchange.new(self, name, options).tap do |x|
|
206
284
|
x.declare!
|
207
285
|
end
|
286
|
+
|
287
|
+
self.register_exchange(dx)
|
208
288
|
end
|
209
289
|
|
210
290
|
# Declares a fanout exchange or looks it up in the cache of previously
|
@@ -222,9 +302,11 @@ module HotBunnies
|
|
222
302
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
223
303
|
# @api public
|
224
304
|
def fanout(name, opts = {})
|
225
|
-
Exchange.new(self, name, opts.merge(:type => "fanout")).tap do |x|
|
305
|
+
dx = Exchange.new(self, name, opts.merge(:type => "fanout")).tap do |x|
|
226
306
|
x.declare!
|
227
307
|
end
|
308
|
+
|
309
|
+
self.register_exchange(dx)
|
228
310
|
end
|
229
311
|
|
230
312
|
# Declares a direct exchange or looks it up in the cache of previously
|
@@ -242,9 +324,11 @@ module HotBunnies
|
|
242
324
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
243
325
|
# @api public
|
244
326
|
def direct(name, opts = {})
|
245
|
-
Exchange.new(self, name, opts.merge(:type => "direct")).tap do |x|
|
327
|
+
dx = Exchange.new(self, name, opts.merge(:type => "direct")).tap do |x|
|
246
328
|
x.declare!
|
247
329
|
end
|
330
|
+
|
331
|
+
self.register_exchange(dx)
|
248
332
|
end
|
249
333
|
|
250
334
|
# Declares a topic exchange or looks it up in the cache of previously
|
@@ -262,9 +346,11 @@ module HotBunnies
|
|
262
346
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
263
347
|
# @api public
|
264
348
|
def topic(name, opts = {})
|
265
|
-
Exchange.new(self, name, opts.merge(:type => "topic")).tap do |x|
|
349
|
+
dx = Exchange.new(self, name, opts.merge(:type => "topic")).tap do |x|
|
266
350
|
x.declare!
|
267
351
|
end
|
352
|
+
|
353
|
+
self.register_exchange(dx)
|
268
354
|
end
|
269
355
|
|
270
356
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -282,9 +368,11 @@ module HotBunnies
|
|
282
368
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
283
369
|
# @api public
|
284
370
|
def headers(name, opts = {})
|
285
|
-
Exchange.new(self, name, opts.merge(:type => "headers")).tap do |x|
|
371
|
+
dx = Exchange.new(self, name, opts.merge(:type => "headers")).tap do |x|
|
286
372
|
x.declare!
|
287
373
|
end
|
374
|
+
|
375
|
+
self.register_exchange(dx)
|
288
376
|
end
|
289
377
|
|
290
378
|
# Provides access to the default exchange
|
@@ -330,9 +418,11 @@ module HotBunnies
|
|
330
418
|
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions guide
|
331
419
|
# @api public
|
332
420
|
def queue(name, options={})
|
333
|
-
Queue.new(self, name, options).tap do |q|
|
421
|
+
dq = Queue.new(self, name, options).tap do |q|
|
334
422
|
q.declare!
|
335
423
|
end
|
424
|
+
|
425
|
+
self.register_queue(dq)
|
336
426
|
end
|
337
427
|
|
338
428
|
# Declares a queue using queue.declare AMQP 0.9.1 method.
|
@@ -477,15 +567,22 @@ module HotBunnies
|
|
477
567
|
end
|
478
568
|
|
479
569
|
def basic_consume(queue, auto_ack, consumer)
|
480
|
-
|
570
|
+
consumer.auto_ack = auto_ack
|
571
|
+
tag = converting_rjc_exceptions_to_ruby do
|
481
572
|
@delegate.basic_consume(queue, auto_ack, consumer)
|
482
573
|
end
|
574
|
+
self.register_consumer(tag, consumer)
|
575
|
+
|
576
|
+
tag
|
483
577
|
end
|
484
578
|
|
485
579
|
def basic_qos(prefetch_count)
|
486
|
-
converting_rjc_exceptions_to_ruby do
|
580
|
+
r = converting_rjc_exceptions_to_ruby do
|
487
581
|
@delegate.basic_qos(prefetch_count)
|
488
582
|
end
|
583
|
+
@prefetch_count = prefetch_count
|
584
|
+
|
585
|
+
r
|
489
586
|
end
|
490
587
|
|
491
588
|
def qos(options={})
|
@@ -726,6 +823,36 @@ module HotBunnies
|
|
726
823
|
end
|
727
824
|
end
|
728
825
|
|
826
|
+
# @private
|
827
|
+
def deregister_queue(queue)
|
828
|
+
@queues.delete(queue.name)
|
829
|
+
end
|
830
|
+
|
831
|
+
# @private
|
832
|
+
def deregister_queue_named(name)
|
833
|
+
@queues.delete(name)
|
834
|
+
end
|
835
|
+
|
836
|
+
# @private
|
837
|
+
def register_queue(queue)
|
838
|
+
@queues[queue.name] = queue
|
839
|
+
end
|
840
|
+
|
841
|
+
# @private
|
842
|
+
def find_queue(name)
|
843
|
+
@queues[name]
|
844
|
+
end
|
845
|
+
|
846
|
+
# @private
|
847
|
+
def deregister_exchange(exchange)
|
848
|
+
@exchanges.delete(exchange.name)
|
849
|
+
end
|
850
|
+
|
851
|
+
# @private
|
852
|
+
def register_exchange(exchange)
|
853
|
+
@exchanges[exchange.name] = exchange
|
854
|
+
end
|
855
|
+
|
729
856
|
# @private
|
730
857
|
def register_consumer(consumer_tag, consumer)
|
731
858
|
@consumers[consumer_tag] = consumer
|
@@ -3,10 +3,13 @@ module HotBunnies
|
|
3
3
|
|
4
4
|
class BaseConsumer < DefaultConsumer
|
5
5
|
attr_accessor :consumer_tag
|
6
|
+
attr_accessor :auto_ack
|
6
7
|
|
7
|
-
def initialize(channel)
|
8
|
+
def initialize(channel, queue)
|
8
9
|
super(channel)
|
9
10
|
@channel = channel
|
11
|
+
@queue = queue
|
12
|
+
@auto_ack = true
|
10
13
|
|
11
14
|
@cancelling = JavaConcurrent::AtomicBoolean.new
|
12
15
|
@cancelled = JavaConcurrent::AtomicBoolean.new
|
@@ -14,8 +17,8 @@ module HotBunnies
|
|
14
17
|
@terminated = JavaConcurrent::AtomicBoolean.new
|
15
18
|
end
|
16
19
|
|
17
|
-
def handleDelivery(consumer_tag, envelope, properties,
|
18
|
-
body = String.from_java_bytes(
|
20
|
+
def handleDelivery(consumer_tag, envelope, properties, bytes)
|
21
|
+
body = String.from_java_bytes(bytes)
|
19
22
|
headers = Headers.new(channel, consumer_tag, envelope, properties)
|
20
23
|
|
21
24
|
deliver(headers, body)
|
@@ -68,5 +71,15 @@ module HotBunnies
|
|
68
71
|
def terminated?
|
69
72
|
@terminated.get
|
70
73
|
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
def recover_from_network_failure
|
77
|
+
@consumer_tag = @channel.basic_consume(@queue.name, @auto_ack, self)
|
78
|
+
|
79
|
+
@terminated.set(false)
|
80
|
+
@cancelled.set(false)
|
81
|
+
|
82
|
+
@consumer_tag
|
83
|
+
end
|
71
84
|
end
|
72
85
|
end
|
@@ -6,8 +6,8 @@ module HotBunnies
|
|
6
6
|
|
7
7
|
POISON = :__poison__
|
8
8
|
|
9
|
-
def initialize(channel, buffer_size, opts, callback)
|
10
|
-
super(channel, callback)
|
9
|
+
def initialize(channel, queue, buffer_size, opts, callback)
|
10
|
+
super(channel, queue, callback)
|
11
11
|
if buffer_size
|
12
12
|
@internal_queue = ArrayBlockingQueue.new(buffer_size)
|
13
13
|
else
|
@@ -2,10 +2,10 @@ require "hot_bunnies/consumers/base"
|
|
2
2
|
|
3
3
|
module HotBunnies
|
4
4
|
class CallbackConsumer < BaseConsumer
|
5
|
-
def initialize(channel, callback)
|
5
|
+
def initialize(channel, queue, callback)
|
6
6
|
raise ArgumentError, "callback must not be nil!" if callback.nil?
|
7
7
|
|
8
|
-
super(channel)
|
8
|
+
super(channel, queue)
|
9
9
|
@callback = callback
|
10
10
|
@callback_arity = @callback.arity
|
11
11
|
end
|
@@ -20,11 +20,20 @@ module HotBunnies
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class AsyncCallbackConsumer < CallbackConsumer
|
23
|
-
def initialize(channel, opts, callback
|
24
|
-
super(channel, callback)
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
def initialize(channel, queue, opts, callback)
|
24
|
+
super(channel, queue, callback)
|
25
|
+
|
26
|
+
# during connection recovery, the executor may be shut down, e.g. due to
|
27
|
+
# an exception. So we need a way to create a duplicate. Unfortunately, since
|
28
|
+
# the executor can be passed as an argument, we cannot know how it was
|
29
|
+
# instantiated. Instead we require a lambda to produce instance of an executor
|
30
|
+
# as a workaround. MK.
|
31
|
+
@executor_factory = opts.fetch(:executor_factory, Proc.new {
|
32
|
+
JavaConcurrent::Executors.new_single_thread_executor
|
33
|
+
})
|
34
|
+
@executor = opts.fetch(:executor, @executor_factory.call)
|
35
|
+
@executor_submit = @executor.java_method(:submit, [JavaConcurrent::Runnable.java_class])
|
36
|
+
@opts = opts
|
28
37
|
end
|
29
38
|
|
30
39
|
def deliver(headers, message)
|
@@ -32,8 +41,9 @@ module HotBunnies
|
|
32
41
|
@executor_submit.call do
|
33
42
|
begin
|
34
43
|
callback(headers, message)
|
35
|
-
rescue Exception => e
|
36
|
-
|
44
|
+
rescue Exception, java.lang.Throwable => e
|
45
|
+
# TODO: logging
|
46
|
+
$stderr.puts "Unhandled exception in consumer #{@consumer_tag}: #{e}"
|
37
47
|
end
|
38
48
|
end
|
39
49
|
end
|
@@ -69,5 +79,15 @@ module HotBunnies
|
|
69
79
|
end
|
70
80
|
alias maybe_shut_down_executor gracefully_shut_down
|
71
81
|
alias gracefully_shutdown gracefully_shut_down
|
82
|
+
|
83
|
+
|
84
|
+
# @private
|
85
|
+
def recover_from_network_failure
|
86
|
+
# ensure we have a functioning executor. MK.
|
87
|
+
@executor = @executor_factory.call
|
88
|
+
@executor_submit = @executor.java_method(:submit, [JavaConcurrent::Runnable.java_class])
|
89
|
+
|
90
|
+
super
|
91
|
+
end
|
72
92
|
end
|
73
93
|
end
|
data/lib/hot_bunnies/exchange.rb
CHANGED
data/lib/hot_bunnies/juc.rb
CHANGED
data/lib/hot_bunnies/queue.rb
CHANGED
@@ -3,33 +3,144 @@
|
|
3
3
|
require "hot_bunnies/juc"
|
4
4
|
require "hot_bunnies/metadata"
|
5
5
|
require "hot_bunnies/consumers"
|
6
|
+
require "set"
|
6
7
|
|
7
8
|
module HotBunnies
|
9
|
+
# Represents AMQP 0.9.1 queue.
|
10
|
+
#
|
11
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
12
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions guide
|
8
13
|
class Queue
|
9
|
-
|
14
|
+
# @return [HotBunnies::Channel] Channel this queue uses
|
15
|
+
attr_reader :channel
|
16
|
+
# @return [String] Queue name
|
17
|
+
attr_reader :name
|
10
18
|
|
19
|
+
# @param [HotBunnies::Channel] channel_or_connection Channel this queue will use.
|
20
|
+
# @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
|
21
|
+
# @param [Hash] opts Queue properties
|
22
|
+
#
|
23
|
+
# @option opts [Boolean] :durable (false) Should this queue be durable?
|
24
|
+
# @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
25
|
+
# @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
26
|
+
# @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
27
|
+
#
|
28
|
+
# @see HotBunnies::Channel#queue
|
29
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
30
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions guide
|
31
|
+
# @api public
|
11
32
|
def initialize(channel, name, options={})
|
12
33
|
@channel = channel
|
13
34
|
@name = name
|
14
35
|
@options = {:durable => false, :exclusive => false, :auto_delete => false, :passive => false, :arguments => Hash.new}.merge(options)
|
36
|
+
|
37
|
+
@durable = @options[:durable]
|
38
|
+
@exclusive = @options[:exclusive]
|
39
|
+
@server_named = @name.empty?
|
40
|
+
@auto_delete = @options[:auto_delete]
|
41
|
+
@arguments = @options[:arguments]
|
42
|
+
|
43
|
+
@bindings = Set.new
|
15
44
|
end
|
16
45
|
|
46
|
+
|
47
|
+
# @return [Boolean] true if this queue was declared as durable (will survive broker restart).
|
48
|
+
# @api public
|
49
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
50
|
+
def durable?
|
51
|
+
@durable
|
52
|
+
end # durable?
|
53
|
+
|
54
|
+
# @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer)
|
55
|
+
# @api public
|
56
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
57
|
+
def exclusive?
|
58
|
+
@exclusive
|
59
|
+
end # exclusive?
|
60
|
+
|
61
|
+
# @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds).
|
62
|
+
# @api public
|
63
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
64
|
+
def auto_delete?
|
65
|
+
@auto_delete
|
66
|
+
end # auto_delete?
|
67
|
+
|
68
|
+
# @return [Boolean] true if this queue was declared as server named.
|
69
|
+
# @api public
|
70
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
71
|
+
def server_named?
|
72
|
+
@server_named
|
73
|
+
end # server_named?
|
74
|
+
|
75
|
+
# @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
76
|
+
# @api public
|
77
|
+
def arguments
|
78
|
+
@arguments
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
# Binds queue to an exchange
|
84
|
+
#
|
85
|
+
# @param [HotBunnies::Exchange,String] exchange Exchange to bind to
|
86
|
+
# @param [Hash] options Binding properties
|
87
|
+
#
|
88
|
+
# @option options [String] :routing_key Routing key
|
89
|
+
# @option options [Hash] :arguments ({}) Additional optional binding arguments
|
90
|
+
#
|
91
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
92
|
+
# @see http://hotbunnies.info/articles/bindings.html Bindings guide
|
93
|
+
# @api public
|
17
94
|
def bind(exchange, options={})
|
18
|
-
exchange_name = if exchange.respond_to?(:name) then
|
19
|
-
|
95
|
+
exchange_name = if exchange.respond_to?(:name) then
|
96
|
+
exchange.name
|
97
|
+
else
|
98
|
+
exchange.to_s
|
99
|
+
end
|
100
|
+
@channel.queue_bind(@name, exchange_name, (options[:routing_key] || options[:key] || ""), options[:arguments])
|
101
|
+
|
102
|
+
# store bindings for automatic recovery. We need to be very careful to
|
103
|
+
# not cause an infinite rebinding loop here when we recover. MK.
|
104
|
+
binding = { :exchange => exchange_name, :routing_key => (options[:routing_key] || options[:key]), :arguments => options[:arguments] }
|
105
|
+
@bindings << binding unless @bindings.include?(binding)
|
20
106
|
|
21
107
|
self
|
22
108
|
end
|
23
109
|
|
110
|
+
# Unbinds queue from an exchange
|
111
|
+
#
|
112
|
+
# @param [HotBunnies::Exchange,String] exchange Exchange to unbind from
|
113
|
+
# @param [Hash] options Binding properties
|
114
|
+
#
|
115
|
+
# @option options [String] :routing_key Routing key
|
116
|
+
# @option options [Hash] :arguments ({}) Additional optional binding arguments
|
117
|
+
#
|
118
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
119
|
+
# @see http://hotbunnies.info/articles/bindings.html Bindings guide
|
120
|
+
# @api public
|
24
121
|
def unbind(exchange, options={})
|
25
|
-
exchange_name = if exchange.respond_to?(:name) then
|
122
|
+
exchange_name = if exchange.respond_to?(:name) then
|
123
|
+
exchange.name
|
124
|
+
else
|
125
|
+
exchange.to_s
|
126
|
+
end
|
26
127
|
@channel.queue_unbind(@name, exchange_name, options.fetch(:routing_key, ''))
|
27
128
|
|
129
|
+
binding = { :exchange => exchange_name, :routing_key => (options[:routing_key] || options[:key] || ""), :arguments => options[:arguments] }
|
130
|
+
@bindings.delete(binding) unless @bindings.include?(binding)
|
131
|
+
|
28
132
|
self
|
29
133
|
end
|
30
134
|
|
31
|
-
|
32
|
-
|
135
|
+
# Deletes the queue
|
136
|
+
#
|
137
|
+
# @option [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
|
138
|
+
# @option [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
|
139
|
+
#
|
140
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
141
|
+
# @api public
|
142
|
+
def delete(if_unused = false, if_empty = false)
|
143
|
+
@channel.queue_delete(@name, if_unused, if_empty)
|
33
144
|
end
|
34
145
|
|
35
146
|
def purge
|
@@ -49,9 +160,13 @@ module HotBunnies
|
|
49
160
|
|
50
161
|
def build_consumer(opts, &block)
|
51
162
|
if opts[:block] || opts[:blocking]
|
52
|
-
BlockingCallbackConsumer.new(@channel, opts[:buffer_size], opts, block)
|
163
|
+
BlockingCallbackConsumer.new(@channel, self, opts[:buffer_size], opts, block)
|
53
164
|
else
|
54
|
-
|
165
|
+
esf = opts.fetch(:executor_factory, Proc.new {
|
166
|
+
JavaConcurrent::Executors.new_single_thread_executor
|
167
|
+
})
|
168
|
+
es = opts.fetch(:executor, esf.call)
|
169
|
+
AsyncCallbackConsumer.new(@channel, self, opts.merge(:executor => es, :executor_factory => esf), block)
|
55
170
|
end
|
56
171
|
end
|
57
172
|
|
@@ -117,5 +232,35 @@ module HotBunnies
|
|
117
232
|
end
|
118
233
|
@name = response.queue
|
119
234
|
end
|
235
|
+
|
236
|
+
# @private
|
237
|
+
def recover_from_network_failure
|
238
|
+
if self.server_named?
|
239
|
+
old_name = @name.dup
|
240
|
+
@name = ""
|
241
|
+
|
242
|
+
@channel.deregister_queue_named(old_name)
|
243
|
+
end
|
244
|
+
|
245
|
+
# puts "Recovering queue #{@name}"
|
246
|
+
begin
|
247
|
+
declare!
|
248
|
+
|
249
|
+
@channel.register_queue(self)
|
250
|
+
rescue Exception => e
|
251
|
+
# TODO: use a logger
|
252
|
+
puts "Caught #{e.inspect} while redeclaring and registering #{@name}!"
|
253
|
+
end
|
254
|
+
recover_bindings
|
255
|
+
end
|
256
|
+
|
257
|
+
# @private
|
258
|
+
def recover_bindings
|
259
|
+
@bindings.each do |b|
|
260
|
+
# TODO: use a logger
|
261
|
+
# puts "Recovering binding #{b.inspect}"
|
262
|
+
self.bind(b[:exchange], b)
|
263
|
+
end
|
264
|
+
end
|
120
265
|
end # Queue
|
121
266
|
end # HotBunnies
|
data/lib/hot_bunnies/session.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "hot_bunnies/shutdown_listener"
|
3
|
+
require "set"
|
3
4
|
|
4
5
|
module HotBunnies
|
5
6
|
java_import com.rabbitmq.client.ConnectionFactory
|
6
7
|
java_import com.rabbitmq.client.Connection
|
7
8
|
java_import java.util.concurrent.ConcurrentHashMap
|
9
|
+
java_import java.util.concurrent.ConcurrentSkipListSet
|
8
10
|
|
9
11
|
# Connection to a RabbitMQ node.
|
10
12
|
#
|
@@ -22,6 +24,9 @@ module HotBunnies
|
|
22
24
|
# API
|
23
25
|
#
|
24
26
|
|
27
|
+
# Default reconnection interval for TCP connection failures
|
28
|
+
DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
|
29
|
+
|
25
30
|
# Connects to a RabbitMQ node.
|
26
31
|
#
|
27
32
|
# @param [Hash] options Connection options
|
@@ -65,7 +70,7 @@ module HotBunnies
|
|
65
70
|
end
|
66
71
|
|
67
72
|
|
68
|
-
new(cf)
|
73
|
+
new(cf, options)
|
69
74
|
end
|
70
75
|
|
71
76
|
# @private
|
@@ -75,14 +80,26 @@ module HotBunnies
|
|
75
80
|
|
76
81
|
|
77
82
|
# @private
|
78
|
-
def initialize(connection_factory)
|
83
|
+
def initialize(connection_factory, opts = {})
|
79
84
|
@cf = connection_factory
|
80
85
|
@connection = converting_rjc_exceptions_to_ruby do
|
81
86
|
self.new_connection
|
82
87
|
end
|
83
88
|
@channels = ConcurrentHashMap.new
|
84
|
-
|
85
89
|
@thread = Thread.current
|
90
|
+
|
91
|
+
# should automatic recovery from network failures be used?
|
92
|
+
@automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
|
93
|
+
true
|
94
|
+
else
|
95
|
+
opts[:automatically_recover] || opts[:automatic_recovery]
|
96
|
+
end
|
97
|
+
@network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL)
|
98
|
+
@shutdown_hooks = ConcurrentSkipListSet.new
|
99
|
+
|
100
|
+
if @automatically_recover
|
101
|
+
self.add_automatic_recovery_hook
|
102
|
+
end
|
86
103
|
end
|
87
104
|
|
88
105
|
# Opens a new channel.
|
@@ -115,13 +132,57 @@ module HotBunnies
|
|
115
132
|
@connection.close
|
116
133
|
end
|
117
134
|
|
135
|
+
def open?
|
136
|
+
@connection.open?
|
137
|
+
end
|
138
|
+
alias connected? open?
|
139
|
+
|
118
140
|
def on_shutdown(&block)
|
119
141
|
sh = ShutdownListener.new(self, &block)
|
142
|
+
@shutdown_hooks << sh
|
143
|
+
|
120
144
|
@connection.add_shutdown_listener(sh)
|
121
145
|
|
122
146
|
sh
|
123
147
|
end
|
124
148
|
|
149
|
+
# @private
|
150
|
+
def add_automatic_recovery_hook
|
151
|
+
fn = Proc.new do |_, signal|
|
152
|
+
if !signal.initiated_by_application
|
153
|
+
self.automatically_recover
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@automatic_recovery_hook = self.on_shutdown(&fn)
|
158
|
+
end
|
159
|
+
|
160
|
+
# @private
|
161
|
+
def disable_automatic_recovery
|
162
|
+
@connetion.remove_shutdown_listener(@automatic_recovery_hook) if @automatic_recovery_hook
|
163
|
+
end
|
164
|
+
|
165
|
+
def automatically_recover
|
166
|
+
# recovering immediately makes little sense. Wait a bit first. MK.
|
167
|
+
java.lang.Thread.sleep(@network_recovery_interval * 1000)
|
168
|
+
|
169
|
+
@connection = converting_rjc_exceptions_to_ruby do
|
170
|
+
self.new_connection
|
171
|
+
end
|
172
|
+
self.recover_shutdown_hooks
|
173
|
+
|
174
|
+
@channels.each do |id, ch|
|
175
|
+
ch.automatically_recover(self, @connection)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# @private
|
180
|
+
def recover_shutdown_hooks
|
181
|
+
@shutdown_hooks.each do |sh|
|
182
|
+
@connection.add_shutdown_listener(sh)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
125
186
|
# Flushes the socket used by this connection.
|
126
187
|
def flush
|
127
188
|
@connection.flush
|
data/lib/hot_bunnies/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot_bunnies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.pre10
|
5
5
|
prerelease: 6
|
6
6
|
platform: java
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-08-11 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: RabbitMQ client for JRuby built around the official RabbitMQ Java client
|
16
16
|
email:
|